Saturday, January 31, 2009

Custom Id generator Class for Hibernate

Custom Id generator Class for Hibernate.

Scenario 1,

Suppose that you are not going to use hibernate built in id generation configuration (e.g. assigned, native, increment, etc ...), then you can use your own id generator class to generate your own id at run time.

To create your own id generator class, first you have to create

your own class that implement hibernate
org.hibernate.id.IdentifierGenerator
interface. Implementing generate method with your own
business requirement and return any Serializable object Value as your id.
(As an example here I use random Long value as my custom id.)
 
Class implementation,
 

package com.danushka.hibernate;

 

import java.io.Serializable;

import org.hibernate.HibernateException;

import org.hibernate.engine.SessionImplementor;

import org.hibernate.id.IdentifierGenerator;

/**

* This class will generate a random value which can be use as Hibernate mapping

* generator value. To use this class ,In the *.hbm.xml file's generator class

* need to change into this class fully qualified name.

*

* @author Danushka.

*

*/

public class CustomGenerator implements IdentifierGenerator {

/**

* This method will generate a random number and return it, hibernate can

* use this id as it generator class id.

*/

@Override

public Serializable generate(SessionImplementor sessionImplemetor,

Object object) throws HibernateException {

Long randNo = (long) Math.random();

return randNo;

}

}

To use this custom id with your hibernate mapping you have to change your hbm.xml file like below.

Example hbm .xml file.

xml version="1.0"?>

DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.danushka.hib.Person" table="Person">

<id name="personId" type="int" column="personId">

<generator class=" com.danushka.hibernate.CustomGenerator " />

id>

<set name="adress">

<key column="personId" />

<one-to-many class="com.danushka.hib.Address" />

set>

class>

hibernate-mapping>

In this hbm.xml file generator class uses my custom ID generator class,

Scenario 2,

If you want to get any table properties which are defined in the hbm.xml file, you have to implements hibernated org.hibernate.id.Configurable Interface. With this interface configure method you can access all the properties available in the hbm.xml file.

In this scenario I am going to call a stored procedure which returns an id for each table, to return id the stored procedure needs table name as input parameter. To get table name at the run time I used configurable interface configure method.

This generate method has the Session Implementer instance as the input parameter, through this instance I can access hibernate session and execute database operations.

Getting table name and calling stored procedure through the hibernate session I can get the next value result as the id of the table. The implementation is given below.

package com.danushka.hibernate.test;

import java.io.Serializable;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;

import org.hibernate.HibernateException;

import org.hibernate.MappingException;

import org.hibernate.dialect.Dialect;

import org.hibernate.engine.SessionImplementor;

import org.hibernate.exception.JDBCExceptionHelper;

import org.hibernate.id.Configurable;

import org.hibernate.id.IdentifierGenerator;

import org.hibernate.id.PersistentIdentifierGenerator;

import org.hibernate.type.Type;

/**

* This class will generate a random value which can be use as Hibernate mapping

* generator value. To use this class ,In the *.hbm.xml file's generator class

* need to change into this class fully qualified name.

*

* @author Danushka.

*

*/

public class CustomGenerator implements IdentifierGenerator, Configurable {

private String tableName;

/**

* This method will generate a random number and return it, hibernate can

* use this id as it generator class id. {@inheritDoc}

*/

@Override

public Serializable generate(SessionImplementor sessionImplemetor,

Object object) throws HibernateException {

return getNextNumber(sessionImplemetor);

}

/**

* This method's parameters have all the available details of hbm.xml

* file.Since all the database table related data available ,Hibernate can

* access to the each and every table. In this example to get my id i used

* procedure. To execute my stored procedure i need to pass table name.

* {@inheritDoc}

*/

@Override

public synchronized void configure(Type type, Properties params, Dialect d)

throws MappingException {

tableName = params.getProperty(PersistentIdentifierGenerator.TABLE);

}

/**

* This method will call the stored procedure and return the next result.

*

* @return next value of the id

*/

private Long getNextNumber(SessionImplementor session) {

String sql = "{call stored procedure name}";

Long nextValue = null;

try {

PreparedStatement st =

session.getBatcher().prepareSelectStatement(sql);

st.setString(1, tableName);

try {

ResultSet rs = st.executeQuery();

try {

while (rs.next()) {

nextValue = Long.parseLong(rs.getString(1));

}

} finally {

rs.close();

}

} finally {

session.getBatcher().closeStatement(st);

}

} catch (SQLException sqle) {

throw JDBCExceptionHelper.convert(session.getFactory()

.getSQLExceptionConverter(), sqle,

"could not fetch initial value for increment generator",

sql);

}

return null;

}

}