Created
October 31, 2017 20:22
-
-
Save nhtzr/5f7747729abf8af1c8472ba5e730ce00 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package us.peopleconnect.microservicecore.util; | |
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.HashMap; | |
import java.util.Map; | |
import org.codehaus.jackson.JsonGenerationException; | |
import org.codehaus.jackson.JsonParseException; | |
import org.codehaus.jackson.map.JsonMappingException; | |
import org.codehaus.jackson.map.ObjectMapper; | |
import org.hibernate.HibernateException; | |
import org.hibernate.engine.spi.SharedSessionContractImplementor; | |
import org.hibernate.type.SerializationException; | |
import org.hibernate.usertype.UserType; | |
import org.postgresql.util.PGobject; | |
import org.springframework.util.ObjectUtils; | |
/** | |
* Usertype to persist objects as JSONB. If the data needs to be cloned, use deepCopy and | |
* also set mutable flag to true. Example usage : metadata field in AuthorizationService | |
* Request Entity. | |
*/ | |
public class AbstractJsonType<T> implements UserType { | |
private ObjectMapper mapper; | |
private Class<T> returnedClass; | |
public AbstractJsonType(Class<T> returnedClass) { | |
this.returnedClass = returnedClass; | |
this.mapper = new ObjectMapper(); | |
} | |
public AbstractJsonType(Class<T> returnedClass, ObjectMapper mapper) { | |
this.returnedClass = returnedClass; | |
this.mapper = mapper; | |
} | |
/** | |
* Return the SQL type codes for the columns mapped by this type. The codes are defined | |
* on <tt>java.sql.Types</tt>. | |
* | |
* @return int[] the typecodes | |
* @see java.sql.Types | |
*/ | |
@Override | |
public int[] sqlTypes() { | |
return new int[] { Types.JAVA_OBJECT }; | |
} | |
/** | |
* The class returned by <tt>nullSafeGet()</tt>. | |
* | |
* @return Class | |
*/ | |
@Override | |
public Class returnedClass() { | |
return returnedClass; | |
} | |
/** | |
* Compare two instances of the class mapped by this type for persistence "equality". | |
* Equality of the persistent state. | |
* | |
* @param x | |
* @param y | |
* @return boolean | |
*/ | |
@Override | |
public boolean equals(Object x, Object y) throws HibernateException { | |
return ObjectUtils.nullSafeEquals(x, y); | |
} | |
/** | |
* Get a hashcode for the instance, consistent with persistence "equality" | |
*/ | |
@Override | |
public int hashCode(Object x) throws HibernateException { | |
if (x == null) { | |
return 0; | |
} | |
return x.hashCode(); | |
} | |
/** | |
* Retrieve an instance of the mapped class from a JDBC resultset. Implementors should | |
* handle possibility of null values. | |
* | |
* @param rs | |
* a JDBC result set | |
* @param names | |
* the column names | |
* @param session | |
* @param owner | |
* the containing entity @return Object | |
* @throws org.hibernate.HibernateException | |
* @throws java.sql.SQLException | |
*/ | |
@Override | |
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, | |
Object owner) throws HibernateException, SQLException { | |
PGobject o = (PGobject) rs.getObject(names[0]); | |
if (o.getValue() != null) { | |
try { | |
return mapper.readValue(o.getValue(), returnedClass); | |
} catch (IOException e) { | |
throw new HibernateException(e); | |
} | |
} | |
return new HashMap<String, String>(); | |
} | |
/** | |
* Write an instance of the mapped class to a prepared statement. Implementors should | |
* handle possibility of null values. A multi-column type should be written to | |
* parameters starting from <tt>index</tt>. | |
* | |
* @param st | |
* a JDBC prepared statement | |
* @param value | |
* the object to write | |
* @param index | |
* statement parameter index | |
* @param session | |
* @throws org.hibernate.HibernateException | |
* @throws java.sql.SQLException | |
*/ | |
@Override | |
public void nullSafeSet(PreparedStatement st, Object value, int index, | |
SharedSessionContractImplementor session) throws HibernateException, SQLException { | |
if (value == null) { | |
st.setNull(index, Types.OTHER); | |
} | |
else { | |
try { | |
String string = mapper.writeValueAsString(value); | |
st.setObject(index, string, Types.OTHER); | |
} | |
catch (IOException e) { | |
throw new HibernateException(e); | |
} | |
} | |
} | |
/** | |
* Return a deep copy of the persistent state, stopping at entities and at collections. | |
* It is not necessary to copy immutable objects, or null values, in which case it is | |
* safe to simply return the argument. | |
* | |
* @param originalValue | |
* the object to be cloned, which may be null | |
* @return Object a copy | |
*/ | |
@Override | |
public Object deepCopy(Object originalValue) throws HibernateException { | |
if (originalValue == null) { | |
return null; | |
} | |
if (!(returnedClass.isInstance(originalValue))) { | |
return null; | |
} | |
try { | |
return mapper.treeToValue(mapper.valueToTree(originalValue), returnedClass); | |
} catch (IOException e) { | |
throw new HibernateException(e); | |
} | |
} | |
/** | |
* Are objects of this type mutable? | |
* | |
* @return boolean | |
*/ | |
@Override | |
public boolean isMutable() { | |
return true; | |
} | |
/** | |
* Transform the object into its cacheable representation. At the very least this method | |
* should perform a deep copy if the type is mutable. That may not be enough for some | |
* implementations, however; for example, associations must be cached as identifier | |
* values. (optional operation) | |
* | |
* @param value | |
* the object to be cached | |
* @return a cachable representation of the object | |
* @throws org.hibernate.HibernateException | |
*/ | |
@Override | |
public Serializable disassemble(Object value) throws HibernateException { | |
Object copy = deepCopy(value); | |
if (copy instanceof Serializable) { | |
return (Serializable) copy; | |
} | |
throw new SerializationException( | |
String.format("Cannot serialize '%s', %s is not Serializable.", value, value.getClass()), | |
null); | |
} | |
/** | |
* Reconstruct an object from the cacheable representation. At the very least this | |
* method should perform a deep copy if the type is mutable. (optional operation) | |
* | |
* @param cached | |
* the object to be cached | |
* @param owner | |
* the owner of the cached object | |
* @return a reconstructed object from the cachable representation | |
* @throws org.hibernate.HibernateException | |
*/ | |
@Override | |
public Object assemble(Serializable cached, Object owner) throws HibernateException { | |
return deepCopy(cached); | |
} | |
/** | |
* During merge, replace the existing (target) value in the entity we are merging to | |
* with a new (original) value from the detached entity we are merging. For immutable | |
* objects, or null values, it is safe to simply return the first parameter. For mutable | |
* objects, it is safe to return a copy of the first parameter. For objects with | |
* component values, it might make sense to recursively replace component values. | |
* | |
* @param original | |
* the value from the detached entity being merged | |
* @param target | |
* the value in the managed entity | |
* @return the value to be merged | |
*/ | |
@Override | |
public Object replace(Object original, Object target, Object owner) throws HibernateException { | |
return deepCopy(original); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment