Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with serializing Hibernate objects using XStream

I've been facing this issue where, the hibernate objects on serialization produces unexpect xmls containing all the instrumented code from Hibernate.

We did some cleaning of the object before serializing the object.

But, is there a standard option available to serialize the object directly?

like image 937
Satya Avatar asked Nov 30 '08 13:11

Satya


4 Answers

I've not used XStream before, but I have serialized Hibernate-managed entities. It isn't fun.

There are two big issues:

  • Lazy loading;
  • One-to-many relationships.

The former is obvious - you need the actual data to serialize. The latter is less so - any one-to-many relationships you declare against collection interfaces (eg: Set<T>) will get plugged by Hibernate's own (unserializable!) collection implementations. This may well be where Hibernate's classes are bleeding into your objects.

I ended up writing reflective code (actually introspective) that did this:

  1. With the session open, touched the entire object graph to force-load any unloaded entities;
  2. Closed the Hibernate session (including any transactions involving its connection);
  3. Walked the object graph, replacing any lists, sets or maps with instances of ArrayList, HashSet or HashMap (known-serializable collections).

Note that step 2 is important - if you replace the collections prior to closing the session, Hibernate will just put its own collections right back upon close...

Edit: @cliff.meyers spotted a detail of the implementation I forgot to mention: if you do this, you need to limit object graph walking only to your own entities, and watch for circular reference paths (eg: by caching references to objects you've already walked).

like image 132
Dan Vinton Avatar answered Nov 02 '22 09:11

Dan Vinton


I've came up with somewhat sufficient solution. In my application, only PersistentSets were messing up XML generated by XStream. So I have added another Converter to XStream (which runs with open Hibernate Session and live objects):

XStream xs = new XStream();
xs.registerConverter(new CollectionConverter(xs.getMapper()) {
    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        org.hibernate.collection.PersistentSet ps = (PersistentSet) source;
        super.marshal(new HashSet(ps), writer, context);
    }

    @Override
    public boolean canConvert(Class type) {
        return type.isAssignableFrom(org.hibernate.collection.PersistentSet.class);
    }
}, XStream.PRIORITY_VERY_HIGH);
String s = xs.toXML(processInstance);

The serialized XML looks like below:

  <processLogs class="org.hibernate.collection.PersistentSet">
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
      <id>813017</id>
      <entryDate>
        <time>1310832421216</time>
        <timezone>GMT</timezone>
      </entryDate>
      <eventI18NKey>process.log.action-performed</eventI18NKey>
      <additionalInfo>Wydrukuj wniosek</additionalInfo>
      <logValue>GENERATE_APPLICATION</logValue>
      <logType>PERFORM_ACTION</logType>
      <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[8]"/>
      <processInstance reference="../../.."/>
      <user reference="../../../creator"/>
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
      <id>808211</id>
      <entryDate>
        <time>1310828206169</time>
        <timezone>GMT</timezone>
      </entryDate>
      <eventI18NKey>process.log.action-performed</eventI18NKey>
      <additionalInfo>Zaakceptuj</additionalInfo>
      <logValue>ACCEPT</logValue>
      <logType>PERFORM_ACTION</logType>
      <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[4]"/>
      <processInstance reference="../../.."/>
      <user reference="../../../creator"/>
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>

In my case, the class attribute was not important, so I have ignored its value. You can of course tinker with it.

like image 43
Tomek Lipski Avatar answered Nov 02 '22 08:11

Tomek Lipski


XStream now provides a separate module/jar to handle Hibernate collections. Please see XStream's FAQ for an example.

like image 43
Mort Avatar answered Nov 02 '22 10:11

Mort


There is some info on this (and sample code) over at the Codehaus JIRA:

http://jira.codehaus.org/browse/XSTR-226

We wrote some tools to work around this sort of issue for a bunch of other remoting implementations (Axis 1, Blaze DS, etc). What we did is very similar to Dan's solution, although we added the ability to declare which object paths to walk and which to "snip" because in many situations we were not interested in all the data; it also would have led to serious issues with the "n+1 selects" problem happening thousands of times! :) I think implementing an XStream converter would be the optimal approach since you'd only have to walk the object graph once. If you set FlushMode.MANUAL on your Session you should also be able to modify the object graph as you go without Hibernate doing anything nasty. Use this with caution though as it's a somewhat advanced technique.

like image 40
cliff.meyers Avatar answered Nov 02 '22 10:11

cliff.meyers