对字节的正确休眠注释[]

2022-08-31 08:24:51

我有一个使用休眠3.1和JPA注释的应用程序。它有一些具有byte[]属性的对象(大小为1k - 200k)。它使用JPA@Lob注释,而休眠3.1可以在所有主要数据库上很好地读取这些内容 - 它似乎隐藏了JDBC Blob供应商的特殊性(正如它应该做的那样)。

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

当我们发现休眠3.5在postgresql中中断(并且不会修复)这个注释组合时,我们不得不升级到3.5(并且没有解决方法)。到目前为止,我还没有找到一个明确的修复程序,但我确实注意到,如果我只是删除@Lob,它使用postgresql类型bytea(有效,但仅适用于postgres)。

annotation                   postgres     oracle      works on
-------------------------------------------------------------
byte[] + @Lob                oid          blob        oracle
byte[]                       bytea        raw(255)    postgresql
byte[] + @Type(PBA)          oid          blob        oracle
byte[] + @Type(BT)           bytea        blob        postgresql

once you use @Type, @Lob seems to not be relevant
note: oracle seems to have deprecated the "raw" type since 8i.

我正在寻找一种方法来获得单个带注释的类(具有blob属性),该类可以跨主要数据库移植。

  • 对 byte[] 属性进行批注的可移植方法是什么?
  • 在某个最新版本的休眠中是否修复了此问题?

更新:阅读此博客后,我终于弄清楚了JIRA问题中的原始解决方法是什么:显然,您应该删除@Lob并将属性注释为:

@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") 
byte[] getValueBuffer() {...

但是,这对不起作用 - 我仍然得到OID而不是bytea;然而,它确实对JIRA问题的作者起作用,他似乎想要oid。

在A. Garcia的答案之后,我尝试了这个组合,它实际上在postgresql上起作用,但在oracle上不起作用。

@Type(type="org.hibernate.type.BinaryType") 
byte[] getValueBuffer() {...

我真正需要做的是控制哪个@org.hibernate.annotations.键入组合(@Lob + byte[]被映射到)到(在postgresql上)。


下面是来自 3.5.5.Final 的片段,来自 MaterializedBlobType(sql 类型 Blob)。根据Steve的博客,postgresql希望你使用Streams for bytea(不要问我为什么)和postgresql的自定义Blob类型。还要注意,在 JDBC 上使用 setBytes() 也适用于 bytea(来自过去的经验)。因此,这就解释了为什么使用流没有影响,它们都假设“bytea”。

public void set(PreparedStatement st, Object value, int index) {
 byte[] internalValue = toInternalFormat( value );
 if ( Environment.useStreamsForBinary() ) {
  // use streams = true
   st.setBinaryStream( index, 
    new ByteArrayInputStream( internalValue ), internalValue.length );
 }
 else {
  // use streams = false
  st.setBytes( index, internalValue );
 }
}

这导致:

ERROR: column "signature" is of type oid but expression is of type bytea

更新下一个合乎逻辑的问题是:“为什么不手动将表定义更改为bytea”并保留(@Lob + byte[])?这确实有效,直到您尝试存储空字节[]。postgreSQL驱动程序认为是OID类型表达式,列类型是bytea - 这是因为hibernate(正确地)调用JDBC.setNull()而不是PG驱动程序期望的JDBC.setBytes(null)。

ERROR: column "signature" is of type bytea but expression is of type oid

休眠状态的类型系统目前是“正在进行的工作”(根据 3.5.5 弃用注释)。事实上,3.5.5 代码中有很多东西被弃用了,很难知道在对 PostgreSQLDialect 进行子类化时要看什么)。

AFAKT, Types.BLOB/'oid' on postgresql 应该映射到一些使用 OID 样式 JDBC 访问的自定义类型(即 PostgresqlBlobType 对象和 NOT MaterializedBlobType)。我从未真正成功地将Blobs与postgresql一起使用,但我确实知道bytea只是简单地工作/我所期望的。

我目前正在查看 BatchUpdateException - 驱动程序可能不支持批处理。


2004年的一句话:“总结一下我的闲言碎语,我想说的是,我们应该等待JDBC驱动程序在更改Hibernate之前正确执行LOB。

引用:


答案 1

对 byte[] 属性进行批注的可移植方法是什么?

这取决于你想要什么。JPA 可以保留未注释的 。来自 JPA 2.0 规范:byte[]

11.1.6 基本注释

批注是映射到数据库列的最简单类型。注释可以应用于以下任何类型的持久属性或实例变量:Java 基元、类型、基元类型的包装器、、byte[]Byte[]、、、、enums 以及实现 的任何其他类型。如第 2.8 节所述,对于这些类型的持久性字段和属性,注释的使用是可选的。如果未为此类字段或属性指定“基本”批注,则将应用“基本”批注的默认值。BasicBasicjava.lang.Stringjava.math.BigIntegerjava.math.BigDecimaljava.util.Datejava.util.Calendarjava.sql.Datejava.sql.Timejava.sql.Timestampchar[]Character[]SerializableBasic

Hibernate会将它“默认”映射到PostgreSQL处理的SQL(或SQL,具体取决于大小?)。VARBINARYLONGVARBINARYColumnbytea

但是,如果要将 存储在大型对象中,则应使用 .从规格:byte[]@Lob

11.1.24 Lob 注释

批注指定持久性属性或字段应作为大型对象保存到数据库支持的大型对象类型。可移植应用程序在映射到数据库类型时应使用批注。注释可以与基本注释结合使用,也可以与元素集合值为基本类型时与注释结合使用。A 可以是二进制或字符类型。该类型是从持久性字段或属性的类型推断出来的,除字符串和字符类型外,默认为 Blob。LobLobLobLobElementCollectionLobLob

Hibernate会将其映射到PostgreSQL用.BLOBoid

在某个最新版本的休眠中是否修复了此问题?

好吧,问题是我不知道问题到底是什么。但我至少可以说,自3.5.0-Beta-2(这是引入更改的地方)以来,3.5.x分支中没有任何变化。

但是我对HHH-4876HHH-4617以及PostgreSQL和BLOB(在javadoc中提到)等问题的理解是,您应该设置以下属性PostgreSQLDialect

hibernate.jdbc.use_streams_for_binary=false

如果你想使用 与(这是我的理解,因为这不是你想要的甲骨文)。你试过这个吗?oidbyte[]@LobVARBINARY

作为替代方案,HHH-4876建议使用已弃用的行为来获取旧行为(Hibernate 3.5之前)。PrimitiveByteArrayBlobType

引用

  • JPA 2.0 规范
    • 第 2.8 节 “映射非关系字段或属性的默认值”
    • 第 11.1.6 节 “基本注释”
    • 第 11.1.24 节 “Lob 注释”

资源


答案 2

这就是O'reilly Enterprise JavaBeans,3.0所说的

JDBC 为这些非常大的对象提供了特殊类型。java.sql.Blob 类型表示二进制数据,java.sql.Clob 表示字符数据。

这里是PostgreSQLDialect源代码

public PostgreSQLDialect() {
    super();
    ...
    registerColumnType(Types.VARBINARY, "bytea");
    /**
      * Notice it maps java.sql.Types.BLOB as oid
      */
    registerColumnType(Types.BLOB, "oid");
}

所以你可以做什么

覆盖 PostgreSQLDialect,如下所示

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

    public CustomPostgreSQLDialect() {
        super();

        registerColumnType(Types.BLOB, "bytea");
    }
}

现在只需定义您的自定义方言

<property name="hibernate.dialect" value="br.com.ar.dialect.CustomPostgreSQLDialect"/>

并使用可移植的 JPA @Lob注释

@Lob
public byte[] getValueBuffer() {

更新

这里已被提取到这里

我有一个在休眠3.3.2中运行的应用程序,应用程序工作正常,所有blob字段都使用oid(java中的byte[])

...

迁移到休眠 3.5 的所有 blob 字段不再工作,服务器日志显示:ERROR org.hibernate.util.JDBCExceptionReporter - 错误:列的类型为 oid,但表达式的类型为 bytea

这可以在这里解释

这个一般不是PG JDBC中的错误而是3.5版本中Hibernate的默认实现的更改。在我的情况下,在连接上设置兼容属性没有帮助

...

更多我在3.5 - beta 2中看到的,我不知道这是否被修复是Hibernate - 没有@Type注释 - 将自动创建oid类型的列,但会尝试将此读作bytea

有趣的是,当他将Types.BOLB映射为bytea(参见CustomPostgreSQLDialect)时,他会得到

无法执行 JDBC 批量更新

插入或更新时


推荐