Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

Mapping a Blob to a byte[]

Hibernate 1.2.3 has built-in support for blobs. Hibernate natively maps blob columns to java.sql.Blob. However, it's sometimes useful to read the whole blob into memory and deal with it as a byte array.

One approach for doing this to create a new UserType as follows.

package mypackage;

import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Types; 
import java.sql.Blob; 

import cirrus.hibernate.Hibernate; 
import cirrus.hibernate.HibernateException; 
import cirrus.hibernate.UserType; 

public class BinaryBlobType implements UserType 
{ 
  public int[] sqlTypes() 
  { 
    return new int[] { Types.BLOB }; 
  }

  public Class returnedClass() 
  { 
    return byte[].class; 
  } 

  public boolean equals(Object x, Object y) 
  { 
    return (x == y) 
      || (x != null 
        && y != null 
        && java.util.Arrays.equals((byte[]) x, (byte[]) y)); 
  } 

  public Object nullSafeGet(ResultSet rs, String[] names, Object owner) 
  throws HibernateException, SQLException 
  { 
    Blob blob = rs.getBlob(names[0]); 
    return blob.getBytes(1, (int) blob.length()); 
  } 

  public void nullSafeSet(PreparedStatement st, Object value, int index) 
  throws HibernateException, SQLException 
  { 
    st.setBlob(index, Hibernate.createBlob((byte[]) value)); 
  } 

  public Object deepCopy(Object value) 
  { 
    if (value == null) return null; 

    byte[] bytes = (byte[]) value; 
    byte[] result = new byte[bytes.length]; 
    System.arraycopy(bytes, 0, result, 0, bytes.length); 

    return result; 
  } 

  public boolean isMutable() 
  { 
    return true; 
  } 

}

The BinaryBlobType will convert a blob into a byte array and back again.

Here's how to use it. First, define an entity that contains a byte[] property:

public class ImageValue
{
  private long id;
  private image byte[];

  public long getId() { return id; }
  public void setId(long id) { this.id = id; }

  public byte[] getImage() { return image; }
  public void setImage(byte[] image) { this.image = image; }
}

Then map a blob column onto the byte[] property:

<class name="ImageValue" table="IMAGE_VALUE">
  <id name="id/>
  <property name="image" column="IMAGE" type="mypackage.BinaryBlobType"/>
</class>

Notes:

1) Blobs aren't cachable. By converting the blob into a byte array, you can now cache the entity.

2) This approach reads the whole blob into memory at once.

3) The above type is known to work for reading blobs out of the db. Other usage patterns might also work.

Comments (GK)

I changed isMutable() to return true, since an array is a mutable object.

The use of setBlob() will work on some drivers, but not all. I think its more portable to use setBytes() or even setBinaryStream().

comments (db)

db's comment above was right, setBlob() didn't work on Oracle, I used setBytes().

comments (Chad Woolley)

Below is a modified nullsafeset() that i needed to use to get it to work with tomcat 4.1.27 & oracle 8/9i - the normal calls don't work through the tomcat/dbcp connection pool wrapper objects... (this caused me great pain)

pls note that the setBytes() doesn't seem to work with oracle driver & hibernate

d.birch@eclipsegroup.com.au

public void nullSafeSet(PreparedStatement st, Object value, int index) 
  throws HibernateException, SQLException 
{ 
  if(st instanceof org.apache.commons.dbcp.DelegatingPreparedStatement && 
    ((org.apache.commons.dbcp.DelegatingPreparedStatement)st).getDelegate()     
       instanceof oracle.jdbc.OraclePreparedStatement)
  {
    oracle.sql.BLOB blob = oracle.sql.BLOB.createTemporary(
    ((org.apache.commons.dbcp.PoolableConnection)st.getConnection()).getDelegate(), false, oracle.sql.BLOB.DURATION_SESSION);
            
    blob.open(BLOB.MODE_READWRITE);
    
    OutputStream out = blob.getBinaryOutputStream();
            
    try
    {
      out.write((byte[])value);
      out.flush();
      out.close();
    }
    catch(IOException e)
    {
      throw new SQLException("failed write to blob" + e.getMessage());
    }

    blob.close();

    ((oracle.jdbc.OraclePreparedStatement)((org.apache.commons.dbcp.DelegatingPreparedStatement)st).getDelegate()).setBLOB(index, blob);
  }
  else
  {
    st.setBlob(index, Hibernate.createBlob((byte[]) value));
  } 
} 

//and.. note the null check, oracle drivers return a null blob...
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) 
  throws HibernateException, SQLException 
{
  final Blob blob = rs.getBlob(names[0]);
  return blob != null?blob.getBytes(1, (int)blob.length()):null; 
}

/ comments Vanitha

I had to use the user type to save pdfs as oraBLOBs in oracle 91 database. nullsafeSet

needed a sligh modification , or else ther was a classcastexception. Used oracle Blob instead of Hibernate Blob type and it works.

 public void nullSafeSet(PreparedStatement st, Object value, int index)
    throws HibernateException, SQLException
  {
    oracle.sql.BLOB t_blob = oracle.sql.BLOB.createTemporary(((org.jboss.resource.adapter.jdbc.WrappedConnection) st.getConnection()).getUnderlyingConnection(),
        false, oracle.sql.BLOB.DURATION_SESSION);
    OutputStream t_out = null;

    t_blob.open(BLOB.MODE_READWRITE);

    t_out = t_blob.getBinaryOutputStream();

    try
    {
      t_out.write((byte[]) value);
      t_out.flush();
      t_out.close();
    }
    catch (IOException e)
    {
      throw new SQLException("failed write to blob" + e.getMessage());
    }

    t_blob.close();

    st.setBlob(index, t_blob);
  }

</code>


  NEW COMMENT

Oracle BLOB 11 Nov 2003, 09:51 l.masini
I modified (db) nullSafeGet to let it work with Oracle that uses BLOB:

....
		else if (st instanceof oracle.jdbc.OraclePreparedStatement)
		{
			oracle.sql.BLOB blob =
				oracle.sql.BLOB.createTemporary(st.getConnection(),
					false,
					oracle.sql.BLOB.DURATION_SESSION);

			blob.open(BLOB.MODE_READWRITE);

			OutputStream out = blob.getBinaryOutputStream();

			try
			{
				out.write((byte[]) value);
				out.flush();
				out.close();
			}
			catch (IOException e)
			{
				throw new SQLException("failed write to blob" + e.getMessage());
			}

			blob.close();

			(
				(oracle
					.jdbc
					.OraclePreparedStatement)
						(st)
					)
					.setBLOB(
				index,
				blob);
		}
.....

Now I am asking if this will work with some kind of connection pooling...
Time will say.
L.
 
Oracle BLOB 02 Jan 2004, 18:40 valeriasalviati
Hi!
I'm working with Hibernate and Oracle 8.
I have a problems working with Blobs...

The sourcecode is:

Blob data;

public Blob getData( )
{
	return data;
}
public void setTheData(byte[] theData) {
   this.data = Hibernate.createBlob(theData);
}

The error is:
Could not synchronize database state with session
java.sql.SQLException: operación no autorizada: streams type cannot be
used in batching


What I should do?

Thank you!
 
Re: Oracle BLOB 08 Jan 2004, 07:41 udoo
disable jdbc batch update by
hibernate.jdbc.batch_size 0
 
Oracle 9i and BLOBs.. 09 Aug 2004, 21:59 greener
I've been working with the Oracle 9i JDBC driver and a Blob column. 
With both the UserType and a blob column mapping, I receive a "fetch out
of sequence" exception:
    java.sql.SQLException: ORA-01002: fetch out of sequence

The driver version is 9.2.0.4.0.

When I debug through the UserType above, I only see the sqlTypes and
returnedClass methods being invoked.  Neither the set nor the get are used.

Suggestions?
 
Oracle10g 06 Apr 2005, 10:25 dannyor
I have tried several of the approaches described:
1. I created the class BinaryBlobType.
2. I added the Oracle specific BLOB creation since the Oracle code
assumes that any java.sql.Blob they get is an oracle.sql.BLOB which is
not the case if you use Hibernate.createBlob().

As someone has mentioned here, the code won't run if you need jdbc
batches since the above code works with streams.

However,  if you don't mind giving up streams and holding the bytes
themselves (which is my case, since I got relatively small byte arrays)
then  using :
[code]
    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        byte[] bytes = (byte[]) value;
        st.setBytes(index, bytes);
    }
[/code]

seems to me as the simplest way
 
Oracle BLOB 26 Apr 2005, 21:00 adzinets
the nullSafeSet method above doesn't check for nulls. Should be:
<code>
  if (value == null) {
      st.setNull(index, Types.BINARY);
  } else {
    //setting BLOB here
    ...
  }
</code>
 
Re: Oracle 9i and BLOBs.. 30 May 2005, 23:52 masoud_omidvar
I found the error description in the http://ora-01002.ora-code.com/

<code>
ORA-01002:
	fetch out of sequence
Cause:
	In a host language program, a FETCH call was issued out of sequence. A
successful parse-and-execute call must be issued before a fetch. This
can occur if an attempt was made to FETCH from an active set after all
records have been fetched. *This may be caused by fetching from a SELECT
FOR UPDATE cursor after a commit*. A PL/SQL cursor loop implicitly does
fetches and may also cause this error.
Action:	Parse and execute a SQL statement before attempting to fetch the
data.
</code>

I had seen the same problem. I've added the following line to make sure
that no transaction has been commited in middle of my sequence of work.
I hope this help you.

sess.connection().setAutoCommit(false);
 
Mapping a Blob to a byte[] 07 Jul 2005, 03:38 hanson.char
One way to get around this blob to byte[] mapping problem in Hibernate:
http://hansonchar.blogspot.com/2005/06/oracle-blob-mapped-to-byte-in.html
 
Creating IMAGE and using byte[] 19 Sep 2008, 15:18 arturotena
I wanted to create a IMAGE column in Sybase, but byte[] in my entity. I
had to change this class:

1. Implemented the hashCode method for a byte[] (read Effective Java).

2. Use st.getBytes() and st.setBytes() in nullSafeGet() and nullSafeSet().

3. Implemented checking value == nullin nullSafeSet(), as another
commenter said.

4. Changed isMutable() to false.

5. Implemented replace(Object original, Object target, Object owner)
method, always returning original.

I'm now a happy byte[] user. =)

I'm using Hibernate 3.2.6.ga with annotations, Sybase (Adaptive Server
Enterprise/12.5.3) and jConnect 5.5.
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]