Although, I'm answering this very late but I managed to achieve this with a very simple way which is working so smooth-
I created a custom Hibernate type which implements UserType
:
package com.wizpanda.hibernate
import groovy.transform.CompileStatic
import org.grails.web.json.JSONObject
import org.hibernate.HibernateException
import org.hibernate.engine.spi.SessionImplementor
import org.hibernate.usertype.UserType
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.Types
/**
* An implementation of {@link org.grails.web.json.JSONObject} column using Hibernate custom types.
* https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#_custom_type
* https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/usertype/UserType.html
*
* @author Shashank Agrawal
*/
@CompileStatic
class JSONObjectFooType implements UserType {
@Override
int[] sqlTypes() {
return [Types.OTHER] as int[]
}
//@SuppressWarnings("rawtypes")
@Override
Class returnedClass() {
return JSONObject.class
}
@Override
boolean equals(Object x, Object y) throws HibernateException {
return x && x.equals(y)
}
@Override
int hashCode(Object x) throws HibernateException {
return x.hashCode()
}
@Override
Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
String value = rs.getString(names[0])
if (!value) {
return null
}
return new JSONObject(value)
}
@Override
void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
String valueToPersist
if (value) {
if (value instanceof JSONObject) {
valueToPersist = value.toString()
} else if (value instanceof String) {
valueToPersist = new JSONObject(value).toString(0)
} else {
throw new HibernateException("Unknown type received for JSONObject based column")
}
}
st.setObject(index, valueToPersist, Types.OTHER)
}
@Override
Object deepCopy(Object value) throws HibernateException {
if (!value) {
return null
}
if (value instanceof JSONObject) {
return new JSONObject(value.toString(0))
}
return value
}
@Override
boolean isMutable() {
return false
}
@Override
Serializable disassemble(Object value) throws HibernateException {
if (value instanceof JSONObject) {
return value?.toString(0)
}
return value?.toString()
}
@Override
Object assemble(Serializable cached, Object owner) throws HibernateException {
if (!cached) {
return null
}
return new JSONObject(cached.toString())
}
@Override
Object replace(Object original, Object target, Object owner) throws HibernateException {
return deepCopy(original)
}
}
I'm using org.grails.web.json.JSONObject
because this is internal from Grails, you can use others like org.json.JSONObject
or Groovy json and replace the occurrences above.
Now, simple use this in your domain class-
class User {
String email
String name
JSONObject settings
static mapping = {
settings type: JSONObjectFooType, sqlType: "text"
}
}
Namaste!