正确的hibernate批注byte

我有一个应用程序使用hibernate3.1和JPA注释。 它有一些带有byte []属性(1k-200k大小)的对象。 它使用JPA @Lob注释,而且hibernate 3.1可以在所有主要数据库上正确读取这些数据,这似乎隐藏了JDBC Blob供应商的特性(就像它应该这样做)。

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

我们不得不升级到3.5,当我们发现,hibernate3.5 打破(并不会修复)这个注解组合postgresql(没有解决方法)。 到目前为止,我还没有find一个明确的解决scheme,但我注意到,如果我只是删除@Lob,它使用postgresqltypesbytea(工作,但只在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 []属性?
  • 这是固定在一些最新版本的hibernate?

更新:在阅读这个博客后,我终于想出了JIRA问题的原始解决方法:显然你应该放弃@Lob并注释属性为:

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

但是,这不适合我 – 我仍然得到OID而不是bytea; 然而它却为JIRA问题的作者工作,他似乎想要oid。

从A.加西亚的答案后,我然后尝试这个组合,实际上在postgresql上工作,但不是在oracle上。

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

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


这里是来自MaterializedBlobType(sqltypesBlob)的3.5.5.Final的片段。 根据Steve的博客,postgresql希望你使用Streams来处理bytea(不要问我为什么)和postgresql的oid自定义Blobtypes。 另请注意,在JDBC上使用setBytes()也适用于bytea(来自过去的经验)。 所以这解释了为什么使用stream没有影响,他们都假设'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驱动程序认为是OIDtypesexpression式,列types是bytea – 这是因为hibernate(正确)调用了JDBC驱动程序期望的JDBC.setNull()而不是JDBC.setBytes(null)。

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

hibernate中的types系统目前是一个“工作正在进行中”(根据3.5.5弃用评论)。 事实上,大多数的3.5.5代码已经被弃用了,当对PostgreSQLDialect进行子分类时,很难知道该看什么)。

AFAKT,postgresql上的Types.BLOB /'oid'应该被映射到一些使用OID风格的JDBC访问(即PostgresqlBlobType对象和NOT MaterializedBlobType)的自定义types。 我从来没有实际上成功地使用blob与postgresql,但我知道bytea只是简单地作为一个/我所期望的。

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


2004年的一句名言:“总结一下我的ramblings,我会说他们应该等待JDBC驱动程序在更改Hibernate之前正确地执行LOB。

参考文献:

  • https://forum.hibernate.org/viewtopic.php?p=2393203
  • https://forum.hibernate.org/viewtopic.php?p=2435174
  • http://hibernate.atlassian.net/browse/HHH-4617
  • http://postgresql.1045698.n5.nabble.com/Migration-to-Hibernate-3-5-final-td2175339.html
  • https://jira.springframework.org/browse/SPR-2318
  • https://forums.hibernate.org/viewtopic.php?p=2203382&sid=b526a17d9cf60a80f13d40cf8082aafd
  • http://virgo47.wordpress.com/2008/06/13/jpa-postgresql-and-bytea-vs-oid-type/

什么是便携式的方式来注释一个byte []属性?

这取决于你想要的。 JPA可以坚持一个没有注释的byte[] 。 从JPA 2.0规范:

11.1.6基本注释

Basic注释是映射到数据库列的最简单的types。 Basic注释可以应用于以下任何types的持久性属性或实例variables:Java基本types,types,基本types的包装, java.lang.Stringjava.math.BigIntegerjava.math.BigDecimaljava.util.Datejava.util.Calendarjava.sql.Datejava.sql.Timejava.sql.Timestampbyte[]Byte[]char[]Character[] ,枚举和其他实现Serializabletypes。 如第2.8节所述,对于这些types的持久字段和属性, Basic注释的使用是可选的。 如果未为此类字段或属性指定Basic注释,则将应用Basic注释的默认值。

而Hibernate会将它默认映射到一个PostgreSQL用bytea处理的SQL VARBINARY (或者是一个取决于Column size的SQL LONGVARBINARY )。

但是如果你想把byte[]存储在大对象中,你应该使用@Lob 。 从规格:

11.1.24 Lob注解

Lob注解指定持久化属性或字段应该作为大型对象持久保存到数据库支持的大对象types。 映射到数据库Lobtypes时,便携式应用程序应该使用Lob注释。 当元素集合值是基本types时, Lob注释可以与Basic注释或ElementCollection注释一起使用。 一个Lob可能是一个二进制或字符types。 Lobtypes是根据持久性字段或属性的types推断出来的,除了string和字符types之外,默认为Blob。

而Hibernate会将它映射到PostgreSQL用oid处理的SQL BLOB

这是固定在一些最新版本的hibernate?

那么问题是我不知道问题到底是什么。 但是我至less可以说在3.5.x分支中的3.5.0-Beta-2(引入了一个改变的地方)之后没有任何改变。

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

 hibernate.jdbc.use_streams_for_binary=false 

如果你想使用oid ie byte[]@Lob (这是我的理解,因为VARBINARY不是你想要的与Oracle)。 你试过这个吗?

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

参考

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

资源

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

对于这些非常大的对象,JDBC有特殊的types。 java.sql.Blobtypes表示二进制数据 ,而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() { 

UPDATE

这里已经提取了

我有一个应用程序运行在hibernate3.3.2和应用程序工作正常 ,所有blob字段使用oid(byte []在Java中)

迁移到hibernate3.5所有Blob字段不再工作 ,并且服务器日志显示:ERROR org.hibernate.util.JDBCExceptionReporter – 错误:列的types为oid,但expression式的types为bytea

这可以在 这里 解释

这一般不是PG JDBC中的bug而是3.5版本中Hibernate默认实现的变化 。 在我的情况设置连接兼容性没有帮助

更多的是我在3.5 – beta 2中看到的,我不知道这是不是固定的是Hibernate – 没有@Type注释 – 将自动创buildoidtypes的列,但会尝试读取它作为bytea

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

无法执行JDBC批量更新

插入或更新时

我终于得到了这个工作。 然而,从A. Garcia的解决scheme扩展而来,因为问题在于hibernatetypes的MaterializedBlobtypes只是映射Blob> bytea是不够的,我们需要replaceMaterializedBlobType,它与hibernate的破解blob支持一起工作。 这个实现只适用于bytea,但是也许JIRA问题中想要OID的人可以贡献一个OID实现。

可悲的是,在运行时replace这些types是一种痛苦,因为它们应该是方言的一部分。 如果只有这个JIRA增加到 3.6将是可能的。

 public class PostgresqlMateralizedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> { public static final PostgresqlMateralizedBlobType INSTANCE = new PostgresqlMateralizedBlobType(); public PostgresqlMateralizedBlobType() { super( PostgresqlBlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE ); } public String getName() { return "materialized_blob"; } } 

这很可能是静态的(getBinder()真的需要一个新的实例吗?),但是我不太了解hibernate的内部,所以这主要是复制+粘贴+修改。

 public class PostgresqlBlobTypeDescriptor extends BlobTypeDescriptor implements SqlTypeDescriptor { public static final BlobTypeDescriptor INSTANCE = new PostgresqlBlobTypeDescriptor(); public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { return new PostgresqlBlobBinder<X>(javaTypeDescriptor, this); } public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { return new BasicExtractor<X>( javaTypeDescriptor, this ) { protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { return (X)rs.getBytes(name); } }; } } public class PostgresqlBlobBinder<J> implements ValueBinder<J> { private final JavaTypeDescriptor<J> javaDescriptor; private final SqlTypeDescriptor sqlDescriptor; public PostgresqlBlobBinder(JavaTypeDescriptor<J> javaDescriptor, SqlTypeDescriptor sqlDescriptor) { this.javaDescriptor = javaDescriptor; this.sqlDescriptor = sqlDescriptor; } ... public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) throws SQLException { st.setBytes(index, (byte[])value); } } 

我使用Hibernate 4.2.7.SP1与Postgres 9.3和以下作品为我:

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

因为甲骨文没有这个问题,对于Postgres我使用自定义方言:

 public class PostgreSQLDialectCustom extends PostgreSQL82Dialect { @Override public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) { return BinaryTypeDescriptor.INSTANCE; } return super.remapSqlTypeDescriptor(sqlTypeDescriptor); } } 

我认为这个解决scheme的优点是,我可以保持冬眠的瓶子不变。

有关Hibernate的更多Postgres / Oracle兼容性问题,请参阅我的博客文章 。

我通过用Postgres的XML文件重写注解来实现它。 为Oracle保留注释。 在我看来,在这种情况下,我们最好重写这个问题的映射 – 一些与xml映射的一致性。 我们可以用xml映射覆盖单个/多个实体。 所以我们将使用我们主要支持的数据库和每个其他数据库的XML文件注释。

注意:我们只需要重写一个类,所以这不是什么大问题。 阅读我的示例更多示例使用XML覆盖注释

在Postgres @Lob打破了字节[],因为它试图将其保存为oid,并且对于string也会发生同样的问题。 下面的代码打破了在oracle上正常工作的postgres。

 @Lob private String stringField; 

 @Lob private byte[] someByteStream; 

为了解决上面的问题,在postgres下面写了自定义的hibernate.dialect

 public class PostgreSQLDialectCustom extends PostgreSQL82Dialect{ public PostgreSQLDialectCustom() { super(); registerColumnType(Types.BLOB, "bytea"); } @Override public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if (Types.CLOB == sqlTypeDescriptor.getSqlType()) { return LongVarcharTypeDescriptor.INSTANCE; } return super.remapSqlTypeDescriptor(sqlTypeDescriptor); } } 

现在在hibernate中configuration自定义方言

 hibernate.dialect=XYZPostgreSQLDialectCustom 

XYZ是包名。

现在它工作正常。 注 – 我的hibernate版本 – 5.2.8.Final Postgres版本9.6.3

我解决了我的问题,通过添加@Lob的注释将在oracle中创buildbyte [] blob,但是这个注释将创build该字段作为oid不能正常工作,使byte []创build为bytea我做客户方言为postgres如下

 Public class PostgreSQLDialectCustom extends PostgreSQL82Dialect { public PostgreSQLDialectCustom() { System.out.println("Init PostgreSQLDialectCustom"); registerColumnType( Types.BLOB, "bytea" ); } @Override public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) { return BinaryTypeDescriptor.INSTANCE; } return super.remapSqlTypeDescriptor(sqlTypeDescriptor); } } 

还需要覆盖方言的参数

spring.jpa.properties.hibernate.dialect = com.ntg.common.DBCompatibilityHelper.PostgreSQLDialectCustom

可以find更多的提示: https : //dzone.com/articles/postgres-and-oracle