I have an application using hibernate 3.1 and JPA annotations. It has a few objects with byte[] attributes (1k - 200k in size). It uses the JPA @Lob annotation, and hibernate 3.1 can read these just fine on all major databases -- it seems to hide the JDBC Blob vendor peculiarities (as it should do).
@Entity
public class ConfigAttribute {
@Lob
public byte[] getValueBuffer() {
return m_valueBuffer;
}
}
We had to upgrade to 3.5, when we discovered that hibernate 3.5 breaks (and won't fix) this annotation combination in postgresql (with no workaround). I have not found a clear fix so far, but I did notice that if I just remove the @Lob, it uses the postgresql type bytea (which works, but only on 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.
I am looking for a way to have a single annotated class (with a blob property) which is portable across major databases.
Update: After reading this blog I have finally figured out what the original workaround in the JIRA issue was: Apparently you are supposed to drop @Lob and annotate the property as:
@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType")
byte[] getValueBuffer() {...
However, this does not work for me -- I still get OIDs instead of bytea; it did however work for the author of the JIRA issue, who seemed to want oid.
After the answer from A. Garcia, I then tried this combo, which actually does work on postgresql, but not on oracle.
@Type(type="org.hibernate.type.BinaryType")
byte[] getValueBuffer() {...
What I really need to do is control which @org.hibernate.annotations.Type the combination (@Lob + byte[] gets mapped) to (on postgresql).
Here is the snippet from 3.5.5.Final from MaterializedBlobType (sql type Blob). According to Steve's blog, postgresql wants you to use Streams for bytea (don't ask me why) and postgresql's custom Blob type for oids. Note also that using setBytes() on JDBC is also for bytea (from past experience). So this explains why use-streams has no affect they both assume '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 );
}
}
This results in:
ERROR: column "signature" is of type oid but expression is of type bytea
Update The next logical question is: "why not just change the table definitions manually to bytea" and keep the (@Lob + byte[])? This does work, UNTIL you try to store a null byte[]. Which the postgreSQL driver thinks is an OID type expression and the column type is bytea -- this is because hibernate (rightly) calls JDBC.setNull() instead of JDBC.setBytes(null) which PG driver expects.
ERROR: column "signature" is of type bytea but expression is of type oid
The type system in hibernate is currently a 'work in progress' (according to 3.5.5 deprecation comment). In fact so much of the 3.5.5 code is deprecated, it is hard to know what to look at when sub-classing the PostgreSQLDialect).
AFAKT, Types.BLOB/'oid' on postgresql should be mapped to some custom type which uses OID style JDBC access (i.e. PostgresqlBlobType object and NOT MaterializedBlobType). I've never actually successfully used Blobs with postgresql, but I do know that bytea just simply works as one / I would expect.
I am currently looking at the BatchUpdateException -- its possible that the driver doesn't support batching.
Great quote from 2004: "To sum up my ramblings, I'd say they we should wait for the JDBC driver to do LOBs properly before changing Hibernate."
References:
What is the portable way to annotate a byte[] property?
It depends on what you want. JPA can persist a non annotated byte[]
. From the JPA 2.0 spec:
11.1.6 Basic Annotation
The
Basic
annotation is the simplest type of mapping to a database column. TheBasic
annotation can be applied to a persistent property or instance variable of any of the following types: Java primitive, types, wrappers of the primitive types,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, and any other type that implementsSerializable
. As described in Section 2.8, the use of theBasic
annotation is optional for persistent fields and properties of these types. If the Basic annotation is not specified for such a field or property, the default values of the Basic annotation will apply.
And Hibernate will map a it "by default" to a SQL VARBINARY
(or a SQL LONGVARBINARY
depending on the Column
size?) that PostgreSQL handles with a bytea
.
But if you want the byte[]
to be stored in a Large Object, you should use a @Lob
. From the spec:
11.1.24 Lob Annotation
A
Lob
annotation specifies that a persistent property or field should be persisted as a large object to a database-supported large object type. Portable applications should use theLob
annotation when mapping to a databaseLob
type. TheLob
annotation may be used in conjunction with the Basic annotation or with theElementCollection
annotation when the element collection value is of basic type. ALob
may be either a binary or character type. TheLob
type is inferred from the type of the persistent field or property and, except for string and character types, defaults to Blob.
And Hibernate will map it to a SQL BLOB
that PostgreSQL handles with a oid
.
Is this fixed in some recent version of hibernate?
Well, the problem is that I don't know what the problem is exactly. But I can at least say that nothing has changed since 3.5.0-Beta-2 (which is where a changed has been introduced)in the 3.5.x branch.
But my understanding of issues like HHH-4876, HHH-4617 and of PostgreSQL and BLOBs (mentioned in the javadoc of the PostgreSQLDialect
) is that you are supposed to set the following property
hibernate.jdbc.use_streams_for_binary=false
if you want to use oid
i.e. byte[]
with @Lob
(which is my understanding since VARBINARY
is not what you want with Oracle). Did you try this?
As an alternative, HHH-4876 suggests using the deprecated PrimitiveByteArrayBlobType
to get the old behavior (pre Hibernate 3.5).