The problem seemed to be hard to solve until I had stumbled across a very neat XML persistence library Xstream. It allows you to persist and restore objects of any type to and from XML without any additional configuration. To learn more about the library I recommend you to skip through their very short but expressive tutorial.
We used Hibernate for persistence, so I needed to teach it how to use Xstream. Thankfully Hibernate is an easily extensible library. I had written a persister that was capable of transparently persisting any object as an array of chars in form of XML.
My solution has a major drawback though, which I haven't fixed, max length of character array is restricted in the DBMS we use (in Oracle it is 4000 chars). So if you need to persist some really long sets of data or just big objects you might have to rewirte the persister to use CBLOB or any other alternative in your database.
package cern.oasis.util.persistence; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.usertype.UserType; import com.thoughtworks.xstream.XStream; /** * Persists any serializable java object as XML string. * * @author Ivan Koblik */ public class XMLString implements UserType { private static final int[] TYPES = { Types.VARCHAR }; private final static XStream stream = new XStream(); static { //Here you can define aliases of some classes if you want to //stream.alias("Impedance", Impedance.class); } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return null == cached ? null : stream.fromXML((String) cached); } @Override public Serializable disassemble(Object value) throws HibernateException { return null == value ? null : stream.toXML(value); } @Override public Object deepCopy(Object value) throws HibernateException { return null == value ? null : stream.fromXML(stream.toXML(value)); } @Override public boolean equals(Object x, Object y) throws HibernateException { if (x == y) { return true; } else if (x == null || y == null) { return false; } else { return x.equals(y); } } @Override public int hashCode(Object x) throws HibernateException { return null == x ? 0 : x.hashCode(); } @Override public boolean isMutable() { return true; } @Override public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { String persistedXml = (String) Hibernate.STRING.nullSafeGet(rs, names[0]); return null == persistedXml ? null : stream.fromXML(persistedXml); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { String xmlToPersist = null == value ? null : stream.toXML(value); if (null != xmlToPersist && xmlToPersist.length() > 4000) { throw new RuntimeException( "Can not persist strings longer then 4000 characters:\n" + xmlToPersist); } Hibernate.STRING.nullSafeSet(st, xmlToPersist, index); } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return this.deepCopy(original); } @Override public Class returnedClass() { return Serializable.class; } @Override public int[] sqlTypes() { return TYPES; } }That's it, the only thing that is left to do is to tell Hibernate to use this persister. Here's an example with annotations:
@Entity public class EntityClass { ... @AccessType("field") @Type(type = "cern.oasis.util.persistence.XMLString") public Map<String, Boolean> getValueThatIsPersistentByXStream() { return this.map; } }Hope you find it useful. Comments are more then welcome!
4 comments:
This sounds very useful to me, but I need a bit more detail.
What class do I annotate with the Type method? The POJO I want to serialize/deserialize to xml?
How do I ask hibernate to give me an object from xml or xml from an object?
Hi,
Type annotation can be used either on the class field or field getter method. Please see here. Conversion to/from XML is completely transparent.
class POJO{
private ZoomWindow digitalZoom;
@Type(type = "cern.oasis.util.persistence.XMLString")
public ZoomWindow getDigitalZoom() {
return digitalZoom;
}
}
could you please give an example how to call it from program or how to use hibernate persitser .?
@Anonymous.
I have updated the example at the end of my post. Hope it helps!
By the way, if all you need is to persist an object in XML without Hibernate, just do this:
XStream xstream = new XStream();
stream.toXML(YOUR_OBJECT);
Post a Comment