If we want to customize our ids or any other columns of the tables, we can do that in Hibernate without adding the extra tables or columns to the table.
Here we are creating the simple example by using the org.hibernate.usertype.UserType, to generate the customized id.
First we are creating the simple table in MYSQL database:
1 |
create table COUPON (ID VARCHAR(20), TYPE VARCHAR(20), VALID_TILL DATE); |
Following is the Coupon bean:
Coupon.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Coupon implements Serializable { private CustomId id; private String type; private Date validTill; public Coupon(CustomId id, String type, Date validTill) { super(); this.id = id; this.type = type; this.validTill = validTill; } // Setters, getters, hashCode, equals go here } |
We will use following CustomId value object to generate our own type of Id
CustomId.java
1 2 3 4 5 6 7 8 9 10 11 12 |
public class CustomId implements Serializable { private int intPart; private String stringPart; public CustomId(int intPart, String stringPart) { super(); this.intPart = intPart; this.stringPart = stringPart; } // Setters, getters, hashCode, equals go here } |
So we want to store the data into the COUPON table with the customized id type, for that we need to implement the interface org.hibernate.usertype.UserType. And need to specify which one is the value class to generate the custom id by using one of this interface method returnedClass().
Following is the implementation for CustomIdType class by providing the implementation for the UserType interface.
CustomIdType.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
public class CustomIdType implements UserType { private int[] types = { StringType.INSTANCE.sqlType() }; @Override public int[] sqlTypes() { return types; } @Override public Class returnedClass() { return CustomId.class; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Serializable disassemble(Object obj) throws HibernateException { return (Serializable) obj; } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public boolean equals(Object obj1, Object obj2) throws HibernateException { return (obj1 == obj2) || (obj1 != null) && (obj2 != null) && (obj1.equals(obj2)); } @Override public int hashCode(Object object) throws HibernateException { return object.hashCode(); } @Override public boolean isMutable() { return false; } @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { CustomId id = (CustomId) rs.getObject(names[0]); return id; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { CustomId id = (CustomId) value; if (id != null) { int intPart = id.getIntPart(); String stringPart = id.getStringPart(); StringType.INSTANCE.nullSafeSet(st, new Integer(intPart) + new String(stringPart), index, session); } } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } } |
The brief description about above methods as follows:
sqlTypes: This method returns an array of int, telling Hibernate which SQL column types to use for persisting the entity properties.
returnedClass: This method specifies which Java value type is mapped by this custom type.
assemble: Hibernate calls this method when the instance is fetched from the second-level cache and converted back from binary serialized to the object form.
disassemble: Hibernate may cache any value-type instance in its second-level cache. For this purpose, Hibernate calls this method to convert the value-type instance to the serialized binary form. Return the current instance if the value type implements the java.io.Serializable interface; otherwise, convert it to a Serializable object.
deepCopy: This method creates a copy of the value type if the value type is mutable; otherwise, it returns the current instance. Note that when you create a copy of an object, you should also copy the object associations and collections.
equals: This method compares two instances of the value type mapped by this custom type to check whether they are equal.
hashCode: This method returns a hashcode for the instance, consistent with persistence equality.
isMutable: This method specifies whether the value type is mutable. Since immutable objects cannot be updated or deleted by the application, defining the value type as immutable allows Hibernate to do some minor performance optimization.
nullSafeGet: This method constructs the value-type instance when the instance is retrieved from the database. resultset is the JDBC ResultSet object containing the instance values, names is an array of the column names queried, and owner is the persistent object associated with the value-type instance. Note that you should handle the possibility of null values.
nullSafeSet: This method is called when the value-type instance is written to a prepared statement to be stored or updated in the database. Handle the possibility of null values. A multi-column type should be written to parameters starting from index.
replace: Assume that the application maintains an instance of the value type that its associated session has already closed. Such objects are not tracked and managed by Hibernate, so they are called detached. Hibernate lets you merge the detached object with a session-managed persistent object through the session’s merge() method. Hibernate calls the replace() method when two instances, detached and session-managed, are merged. The first and second arguments of this method are value-type instances associated with a detached and session-managed persistent object, respectively. The third argument represents the owner object, the persistent object that owns the original value type: Coupon in our case. This method replaces the existing (target) value in the persistent object we are merging with a new (original) value from the detached persistent object we are merging. For immutable objects or null values, return the first argument. For mutable objects, return at least a copy of the first argument through the deepCopy() method.
We need to configure the Hibernate configuration file. The example of configuration file as follows,
hibernate.cfg.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/test </property> <property name="connection.username">root</property> <property name="connection.password">123456</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hibernate.hbm2ddl.auto">update</property> <!-- mapping resources --> <mapping resource="coupon.hbm.xml"/> <mapping resource="customIdType.hbm.xml"/> </session-factory> </hibernate-configuration> |
We need to write the mapping configuration for the Coupon to COUPON table, so as follows,
coupon.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="UTF-8"?> <hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping http://www.hibernate.org/xsd/hibernate-mapping/hibernate-mapping-4.0.xsd"> <class name="com.hibernate.model.Coupon" table="COUPON"> <id name="id" type="com.hibernate.customType.CustomIdType" column="ID" /> <property name="type" type="string" column="TYPE" /> <property name="validTill" type="date" column="VALID_TILL" /> </class> </hibernate-mapping> |
And also we need to write the mapping for CustomIdType to tell the hibernate we are using the CustomId as one of the type for our application like as follows,
cutomIdType.hbm.xml:
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="UTF-8"?> <hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping http://www.hibernate.org/xsd/hibernate-mapping/hibernate-mapping-4.0.xsd"> <typedef class="com.hibernate.customType.CustomIdType" name="CustomIdType" /> </hibernate-mapping> |
OK, all set. This is the time to test our application, following is the test client program using the Java main method.
CouponCustomIDTest.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class CouponCustomIDTest { public static void main(String[] args) { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); CustomId id = new CustomId(102, "ABC"); Coupon coupon = new Coupon(id, "food", new Date()); Serializable cid = session.save(coupon); tx.commit(); session.close(); sessionFactory.close(); } } |
Output:
1 2 3 4 5 |
+------------+-------+--------------+ | ID | TYPE | VALID_TILL | +------------+-------+--------------+ | 102ABC | food | 2017-02-01 | +------------+-------+--------------+ |
Hey, don’t forget to have hibernate cfg file available to classpath.
All The Best 🙂