A Custom reuseable LoadableDetachableModel for Spring/Hibernate

May 21st, 2008 by  |  Published in Hibernate, Spring, Wicket  |  2 Comments

the annoyance


org.hibernate.LazyInitializationException - could not initialize proxy - the owning Session was closed
org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed

the problem

Generally, detached objects in Hibernate are a bad thing when it comes to reusing objects between web-requests. Detached objects are objects that have been persistent but are not attached to a Hibernate session. They’re easily produced but can be a mess if it comes to Lazy-Loading or updating them. Just consider the following case


Foo foo = _myDAO.getFoo(); // attached object

add(new Link("fooLink", new Model(foo)) {

  protected void onClick() {
    Foo foo = (Foo) getModelObject(); // now it's detached as this is called in the next request
    Bar bar = foo.getBar(); // possible lazy-loading exception if bar gets loaded lazily

  }
}

the solution

First be sure to use the OpenSessionInViewFilter sothat a new hibernate session is opened when the request gets attached and closed when it gets detached.

Then the solution of course is a to use a LoadableDetachableModel that gets a fresh instance from the persistence-layer on each request. Could look like that:


IModel fooModel = new LoadableDetachableModel() {

  protected Object load() {
    return _myDAO.getFoo();
  }
}

add(new Link("fooLink", fooModel) {

  protected void onClick() {
    Foo foo = (Foo) getModelObject(); // attached as LDM has loaded a fresh instance
    Bar bar = foo.getBar(); // no lazy loading exception as still attached

  }
}

the improvement

LoadableDetachableModels are a nice thing but it’s quite a lot of work to inject your DAO (or service) and then create a new LDM for each of your objects in each and every one of your panels. so we came up with a more generic version of the LoadableDetachableModel. First, let your model-objects implement an Interface, say IPersistentObject with the single method


  Serializable getId();

As your object are likely to have a primary key named id it’ll be no problem anyway. Then create the following class:


public class PersistentObjectModel extends LoadableDetachableModel {

  private static final long serialVersionUID = 1L;

  private final Class _clazz;

  private final Serializable _id;

  @SpringBean(name = "myDao")
  private IMyDao _myDao;

  @SuppressWarnings("unchecked")
  public PersistentObjectModel(final T object) {
    super(object);

// object may be a hibernate proxy, so get the actual class
    if (object instanceof HibernateProxy) {
      _clazz = (Class) ((HibernateProxy) object).getHibernateLazyInitializer()
.getImplementation().getClass();
    } else {
      _clazz = (Class) object.getClass();
    }
    _id = object.getId();
    // inject the bean
    InjectorHolder.getInjector().inject(this);
  }

  public PersistentObjectModel(final Class clazz, final Serializable id) {
    clazz = clazz;
    _id = id;
    // inject the bean
    InjectorHolder.getInjector().inject(this);
  }

  @Override
  protected T load() {
    // the DAO then simply passes the params to hibernate (or spring's hibernatetemplate
    return _myDao.getById(_clazz, _id);
  }

  @SuppressWarnings("unchecked")
  @Override
  public T getObject() {
    return (T) super.getObject();
  }

}

This makes working with hibernate objects really easy. There are two options:

  1. Use the object e.g.

    setModel(new PersistentObjectModel(foo));

  2. Use only class and id.

    This might happen e.g. if you pass your id using PageParameters (but better encrypt it ;-) )
    setModel(new PersistentObjectModel(Foo.class, 1));

Hope that saved you some time writing boilerplate code ;-) have fun

Responses

  1. Vitor says:

    August 26th, 2011 at 5:40 pm (#)

    Excelent!
    I have a problem similar to that, but I’m not using a Hibernate conection, I’m using a tier named ServiceFacade, that is a Spring POJO with @Service, so, could you give some help do adapt that solution to my case?

    Any help will be very appreciated!

    Thanks a lot and Congratulations for this solution!

    Vitor

  2. Vitor says:

    August 26th, 2011 at 6:21 pm (#)

    Forget!

    I solved just calling InjectorHolder.getInjector().inject(this);
    before the calling of my ServiceFacade tier.

    You solution solved my problem indirectly!
    Thanks a lot!

Leave a Response