import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.base.Objects;
/**
* Hibernate {@link UserType} implementation to handle JSON objects
*
* @see https
* : * UserType.html
*/
@SuppressWarnings("serial")
public class JsonType implements UserType, ParameterizedType, Serializable {
private static final ObjectMapper MAPPER = Jackson2ObjectMapperBuilder
.json().build();
private static final String CLASS_TYPE = "classType";
private static final String TYPE = "type";
private JavaType javaType;
private int sqlType = Types.VARCHAR;
@Override
public void setParameterValues(Properties params) {
String classTypeName = params.getProperty(CLASS_TYPE);
this.javaType = TypeFactory.defaultInstance().constructFromCanonical(
classTypeName);
String type = params.getProperty(TYPE);
if (type != null) {
this.sqlType = Integer.decode(type).intValue();
}
}
@Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return this.deepCopy(cached);
}
@Override
public Object deepCopy(Object value) throws HibernateException {
Object copy = null;
if (value != null) {
try {
return MAPPER.readValue(MAPPER.writerFor(javaType)
.writeValueAsString(value), this.javaType);
} catch (IOException e) {
throw new HibernateException("unable to deep copy object", e);
}
}
return copy;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
try {
return MAPPER.writerFor(javaType).writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new HibernateException("unable to disassemble object", e);
}
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
return EqualsHelper.equals(x, y);
}
@Override
public int hashCode(Object x) throws HibernateException {
return Objects.hashCode(x);
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Object obj;
if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
byte[] bytes = rs.getBytes(names[0]);
if (rs.wasNull() || bytes == null) {
obj = null;
} else {
try {
obj = MAPPER.readValue(bytes, this.javaType);
} catch (IOException e) {
throw new HibernateException(
"unable to read object from result set", e);
}
}
} else {
try {
final String string = rs.getString(names[0]);
if (rs.wasNull()) {
obj = null;
} else {
obj = MAPPER.readValue(string, this.javaType);
}
} catch (IOException e) {
throw new HibernateException(
"unable to read object from result set", e);
}
}
return obj;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, this.sqlType);
} else {
if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
try {
st.setBytes(index, MAPPER.writeValueAsBytes(value));
} catch (JsonProcessingException e) {
throw new HibernateException(
"unable to set object to result set", e);
}
} else {
try {
st.setString(index, MAPPER.writerFor(javaType)
.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new HibernateException(
"unable to set object to result set", e);
}
}
}
}
@Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return this.deepCopy(original);
}
@Override
public Class<?> returnedClass() {
return this.javaType.getRawClass();
}
@Override
public int[] sqlTypes() {
return new int[] { sqlType };
}
}