问题
我有一个使用hibernate 3.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,当我们在postgresql中发现hibernate 3.5breaks (and won't fix)这个注释组合时(没有解决方法)。到目前为止我还没有找到明确的解决方法,但我注意到如果我只是删除了@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 []属性的可移植方式是什么?
- 这是在最近的一些hibernate版本中修复的吗?
**更新:**阅读this blog后我终于找到了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.Type组合(@Lob byte []被映射)到(在postgresql上)。
这是来自MaterializedBlobType(sql类型Blob)的3.5.5.Final的片段。根据Steve的博客,postgresql希望你使用Streams for bytea(不要问我为什么)和postgresql的自定义Blob类型为oids。另请注意,在JDBC上使用setBytes()也适用于bytea(来自过去的经验)。所以这解释了为什么use-streams没有影响他们都假设'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 [])? Thisdoeswork,UNTIL你尝试存储空字节[]。 postgreSQL驱动程序认为是OID类型表达式,列类型是bytea - 这是因为hibernate(正确)调用JDBC.setNull()而不是PG驱动程序期望的JDBC.setBytes(null)。
ERROR: column "signature" is of type bytea but expression is of type oid
hibernate中的类型系统目前是"正在进行中的工作"(根据3.5.5弃用评论)。实际上很多3.5.5代码都被弃用,很难知道在对PostgreSQLDialect进行子类化时应该注意什么。
postgresql上的AFAKT,Types.BLOB /'oid'应该映射到一些使用OID样式JDBC访问的自定义类型(即PostgresqlBlobType对象和NOT MaterializedBlobType)。我从来没有真正成功地使用blog和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/
#1 热门回答(56 赞)
注释byte []属性的可移植方式是什么?
这取决于你想要什么。 JPA可以持久保存未注释的byte[]
。从JPA 2.0规范:
11.1.6基本注释基本注释是到数据库列的最简单的映射类型。 Basic注释可以应用于以下任何类型的持久属性或实例变量:Java原语,类型,基本类型的包装,java.lang.String,java.math.BigInteger,java.math.BigDecimal,java .util.Date,java.util.Calendar,java.sql.Date,java.sql.Time,java.sql.Timestamp,byte [],Byte [],char [],Character [],enums和任何其他实现Serializable的类型。如2.8节所述,对于这些类型的持久字段和属性,Basic注释的使用是可选的。如果未为此类字段或属性指定基本注释,则将应用基本注释的默认值。
Hibernate会将它"默认"映射到PostgreSQL用abytea
处理的SQLVARBINARY
(或SQLLONGVARBINARY
,取决于Column
size?)。
但是如果你想将byte[]
存储在一个大对象中,你应该使用a@Lob
。从规格:
11.1.24 Lob注释Lob注释指定持久属性或字段应作为大对象持久保存到数据库支持的大对象类型。在映射到数据库Lob类型时,便携式应用程序应使用Lob批注。当元素集合值是基本类型时,Lob注释可以与Basic注释结合使用或与ElementCollection注释结合使用。 Lob可以是二进制或字符类型。 Lob类型是从持久字段或属性的类型推断出来的,除字符串和字符类型外,默认为Blob。
Hibernate会将它映射到带有a14211515的PostgreSQL句柄的SQLBLOB
。
这是在最近的一些hibernate版本中修复的吗?
好吧,问题是我不知道究竟是什么问题。但我至少可以说自3.5.0分支以来3.5.0-Beta-2(这是已经引入更改的地方)以来没有任何变化。
但我对诸如HHH-4876,HHH-4617和PostgreSQL and BLOBs(在47575736的javadoc中提到)等问题的理解是你应该设置以下属性
hibernate.jdbc.use_streams_for_binary=false
如果你想使用oid
i.e.byte[]
with@Lob
(这是我的理解,因为VARBINARY
不是你想要的Oracle)。你试过这个吗?
作为替代方案,HHH-4876使用deprecatedPrimitiveByteArrayBlobType
来获取旧行为(在Hibernate 3.5之前)。
###参考文献
- JPA 2.0规范第2.8节"映射非关系字段或属性的默认值"第11.1.6节"基本注释"第11.1.24节"Lob注释"
###资源
- http://opensource.atlassian.com/projects/hibernate/browse/HHH-4876
- http://opensource.atlassian.com/projects/hibernate/browse/HHH-4617
- http://relation.to/Bloggers/PostgreSQLAndBLOBs
#2 热门回答(9 赞)
这就是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() {
更新
这里已被提取here
我有一个在hibernate 3.3.2中运行的应用程序,应用程序工作正常,所有blob字段都使用oid(java中的byte [])
...
迁移到hibernate 3.5所有blob字段都不再起作用,服务器日志显示:ERROR org.hibernate.util.JDBCExceptionReporter - ERROR:列的类型为oid,但表达式的类型为bytea
,可以解释here
这通常不是PG JDBC中的错误,而是改变了3.5版本中Hibernate的默认实现。在我的情况下设置连接上的兼容属性没有帮助。
...
我在3.5 - beta 2中看到的更多,我不知道这是否已修复是Hibernate - 没有@Type注释 - 将自动创建oid类型的列,但会尝试将其读作bytea
有趣的是因为当他将Types.BOLB映射为bytea(参见CustomPostgreSQLDialect)时,他得到了
无法执行JDBC批量更新
插入或更新时
#3 热门回答(6 赞)
我终于有了这个工作。它扩展了A. Garcia的解决方案,但是,由于问题在于hibernate类型MaterializedBlob类型只是映射Blob> bytea是不够的,我们需要替换MaterializedBlobType,它适用于hibernates破碎的blob支持。这个实现只适用于bytea,但是想要OID的JIRA问题的人可能会提供OID实现。
遗憾的是,在运行时替换这些类型是一件痛苦的事情,因为它们应该是Dialect的一部分。如果只有this JIRA enhanement进入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);
}
}