A Hibernate UserType defines a (custom) way how to save Java objects into the database. Basically, it defines how the object is serialized to be persisted in the database and how the object is (re)created from the serialized data stored in the database.
Here is a UserType that I’ve written to persist objects of type JSONObject. I’m using the JSON implementation from json.org; for other JSON libraries the UserType should be very similar.
package net.janjonas.example;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Hibernate user type to persist JSONObject
*
* @see http://www.json.org/javadoc/org/json/JSONObject.html
* @see http://docs.jboss.org/hibernate/stable/annotations/api/org/hibernate/usertype/UserType.html
* @author "Jan Jonas "
*/
public class JSONObjectUserType implements UserType {
private static final int[] SQL_TYPES = { Types.LONGVARCHAR };
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return deepCopy(cached);
}
@Override
public Object deepCopy(Object value) throws HibernateException {
if (value == null) return value;
try {
return new JSONObject(((JSONObject)value).toString());
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return ((JSONObject)value).toString();
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == null) return (y != null);
return (x.equals(y));
}
@Override
public int hashCode(Object x) throws HibernateException {
return ((JSONObject)x).toString().hashCode();
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
if (!rs.wasNull()) {
try {
return new JSONObject(rs.getString(names[0]));
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
return null;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, SQL_TYPES[0]);
} else {
st.setString(index, ((JSONObject)value).toString());
}
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return deepCopy(original);
}
@Override
@SuppressWarnings("unchecked")
public Class returnedClass() {
return JSONObject.class;
}
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
}
3 responses to “Hibernate & UserType: How to persist JSON objects”
very useful, thanks for the info!
couple of things I’ve discovered:
I think there’s a bug in equals() – should it be:?
if (x == null) return (y == null);
also I think there’s a bug in nullSafeGet(): rs.wasNull() refers to the last call, not the one it’s about to read.
this will return null if the last column read returned null, ie some other random column that hibernate happened to read from its resultset immediately prior to this one.
a fix would be to check the results of the rs.getString() for null and return a null JSONObject. or call wasNull() after the getString().
please provide the hbm.xml file sample also.
I have used :
which leads to following exception:
Exception in thread “main” java.lang.ExceptionInInitializerError
at utils.HibernateUtils.buildSessionFactory(HibernateUtils.java:20)
at utils.HibernateUtils.(HibernateUtils.java:6)
at client.Client.main(Client.java:20)
Caused by: org.hibernate.MappingException: Could not determine type for: JSONObjectUserType, at table: ELEMENT, for columns: [org.hibernate.mapping.Column(jsonObject)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:292)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:276)
at org.hibernate.mapping.Property.isValid(Property.java:207)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:458)
at org.hibernate.mapping.RootClass.validate(RootClass.java:215)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1135)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1320)
at utils.HibernateUtils.buildSessionFactory(HibernateUtils.java:17)
… 2 more
Hi, I’m trying to use this sample, but I always received the folow error:
ERROR: column “Tramites” is the type json but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
May you help me?