Blobs and Hibernate

Database tables that include blobs (and clobs) require special attention in Hibernate. The Java API for blobs allows for read/write access to blob data using streams, but that API can only be invoked while the original connection/statement is still open. In Hibernate terms, the original Hibernate Session must still be open.

In a lot of web architectures, this approach is problematic because developers often use Hibernate persistent classes outside of the original Hibernate session.

I’ve used two primary strategies for handling blobs in Hibernate:

  1. read the blob from the database and hold it as a byte array
  2. lazily fetch the blob contents as required.

Both of these approaches have down-sides.

The primary down-side of the first approach relates to extremely large blob values. Consider, for example, a 1 Gigabyte movie file. Holding a 1 Gigabyte movie file as a byte array is going to take about a Gigabyte of RAM. That’s a lot of RAM; more than the typical memory allocation of a standard Java application.

The primary down-side of the second approach relates to updatability of the blob records. It becomes harder to issue updates against a database record that includes a blob.

Let’s try to describe these two approaches in more detail.

Blob to Byte Array

I’ve seen two common ways of handling the blob to byte array conversion:

  1. use a pseudo-Blob property; and
  2. use a blob user type.

Pseudo-Blob Property

The first strategy is fairly simple. Create a persistent bean, like so:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;

import org.hibernate.Hibernate;

public class LabelledPhoto {

  private String label;
  private byte[] image;

  public String getLabel() {
    return this.label;
  }
  public void setLabel(String label) {
    this.label = label;
  }
  public byte[] getImage() {
    return this.image;
  }
  public void setImage(byte[] image) {
    this.image = image;
  }
  @SuppressWarnings("unused")
  private void setBlob(Blob imageBlob) {
    this.image = toByteArray(imageBlob);
  }
  @SuppressWarnings("unused")
  private Blob getBlob(){
    return Hibernate.createBlob(this.image);
  }
  private byte[] toByteArray(Blob fromImageBlob) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
      return toByteArrayImpl(fromImageBlob, baos);
    } catch (Exception e) {
    }
    return null;
  }

  private byte[] toByteArrayImpl(Blob fromImageBlob,
      ByteArrayOutputStream baos) throws SQLException, IOException {
    byte buf[] = new byte[4000];
    int dataSize;
    InputStream is = fromImageBlob.getBinaryStream();

    try {
      while((dataSize = is.read(buf)) != -1) {
        baos.write(buf, 0, dataSize);
      }
    } finally {
      if(is != null) {
        is.close();
      }
    }
    return baos.toByteArray();
  }
}

Part of the trick to this class is that Hibernate is able to invoke private getters and setters to set properties. As a result, the “label” and “blob” properties will be mapped to columns on the database table, while the “image” property will not.

Instances of this class do not keep any reference to Blob object. It is only referenced during invocations of the setBlob/getBlob calls. And because those methods are private, we’re pretty much guaranteed that they’ll only be called by Hibernate in the context of a Session.

Create a Blob User Type

Blob user types are slightly more appealing than the above approach, as you can create a user type class that encapsulates all the Hibernate ugliness and end up with a much cleaner-looking persistent class.

Here is a link for Blob User Types:

  1. Blog User Type on i-proving.com

User types allow you to add additional functionality at the time the blob is read or saved. For example, because blobs are often used to store files, you can save some disk space by performing a compression on the bytestream as you write it to the database. The persistent class and all of its clients remain unaware of the compression; compression is a “dirty little secret” of the Hibernate configuration and user type.

Print Friendly
This entry was posted in Technology and tagged by BC Holmes. Bookmark the permalink.

About BC Holmes

BC Holmes is an architect with 20+ years experience designing and building applications, currently specializing in eHealth and mobile applications. She has worked in the IT Services industry and the financial services industry. She holds a joint honours in Pure Mathematics and Theatre Arts. BC has presented papers at JavaOne ("Developing an Internet Architecture for the Enterprise", "Fast Feedback Loop: JUnit Test Strategies for Tapestry"), XP Toronto ("Good Design: Know It When I See It?") and the Toronto WebSphere Users' Group ("Extreme WebSphere").

Leave a Reply