Detaching and Attaching Persistent Objects on Serialization

March 25th, 2009 by  |  Published in Hibernate, Java, Wicket

Today I’ve received a mail by someone interested in a serialization mechanism I described some days ago. This mechanism was implemented to avoid dealing with detached Hibernate objects in different HTTP (i.e. Wicket) requests (it’s not restricted to Hibernate though). The idea is quite simple: detach objects if they are persistent, serialize them if they are transient, deserialize and attach for the next request – all transparently. So far, this isn’t very special and doesn’t justify such a complicated approach. However, it’s the only way of attaching and detaching object graphs that consist of transient and persistent objects I know.
The mechanism uses the magic serialization hooks readResolve() and writeReplace() (see jGuru). Knowing these hooks (yeah, knowing … as it is probably harder to know them than to actually use them), the rest is quite easy. Replace persistent object with a placeholder but serialize regular objects:

public final Object writeReplace() throws ObjectStreamException {
  if (isTransient(this)) {
    return this;
  } else {
    return new ReplaceHolder(getClass(), getId());
  }
}

The ReplaceHolder – isn’t it a lovely name? – is responsible for replacing itself with the persistent object we didn’t want to serialize before:

public static final class ReplaceHolder implements Serializable {
 
  private static final long serialVersionUID = 1L;
 
  private final String _entityName;
  private final Serializable _id;
 
  public ReplaceHolder(final Class entity, final Serializable id) {
    _entityName = entity.getName();
    _id = id;
  }
 
  public Serializable getId() {
    return _id;
  }
 
  @SuppressWarnings("unchecked")
  public Class getEntity() throws ClassNotFoundException {
    return (Class) Class.forName(_entityName);
  }
 
  public final Object readResolve() throws ObjectStreamException {
    try {
      return loadEntity(getEntity(), getId());
    } catch (final ClassNotFoundException e) {
      throw new InvalidClassException(_entityName, e.getClass().getSimpleName() + ": " + e.getMessage());
    }
  }
}

And that’s it. Well, not completely, but see the attached .java file for details.

There is only one thing Wicket developers have to be aware of. Objects aren’t serialized between requests – don’t confuse it with model detaching. If I remember correctly, there is always one page kept detached but un-serialized. So if one would like to use the serialization, one has to make sure, that the objects are already serialized as soon as the models are detached. For this purpose, I implemented a special Model, that serializes its object on detach:

public void detach() {
  if (_object != null) {
    try {
      final ByteArrayOutputStream bs = new ByteArrayOutputStream();
      new ObjectOutputStream(bs).writeObject(_object);
      _bytes = bs.toByteArray();
      _object = null;
    } catch (final IOException e) {
      throw new WicketRuntimeException(e);
    }
  }
}

You might say that this feels like a hack – at least that’s what I think of it 🙂 But it works perfectly, especially for complex forms where you add persistent objects to transient ones. Furthermore, you make sure that you’ll never serialize a huge persistent object graph into your Wicket session, which definetely is a Bad Thing ™.

Leave a Response