Recent Posts

Tuesday, 7 June 2016

Hibernate Generator Classes


* Generator classes are used to generate the 'identifier' for a persistent object. i.e., While saving an object into the database, the generator informs to the hibernate that, how the primary key value for the new record is going to generate.

* Hibernate using different primary key generator algorithms, for each algorithm internally a Java class is there for its implementation.

* All Generator classes has to implement 'org.hibernate.id.IdentifierGenerator interface, And has to override generate() method. The logic to generate 'identifier' has to write in this method

* In built-in generator classes, identifier generation logic has implemented by using JDBC.

* If we want we can write the user defined generator class and it should implement 'org.hibernate.id.IdentifierGenerator' interface and has to override generate() method.

* To configure generator class we can use <generator /> tag, which is the sub element of <id/> tag.

* <generator/> tag has one attribute called "class" with which we can specify generator class-name.

* While configuring <generator /> tag in mapping file, if we need to pass any parameters to generator class then we ! can use <param /> tag, which i s the sub element of <generator/> tag,
Example: Hibernate Mapping File(HBM)
<hibernate-mapping> 
   <class ...> 
      <id ...> 
         <generator class="generator-class-name">
            <param name="param-name">param value</param>
         </generator> 
      </id> 
       .....     
   </class> 
</hibernate-mapping> 
Hibernate built-in generator classes
1. assigned
2. increment
3. sequence
4. hilo
5. native
6. identity
7. seqhilo
8. uuid
9. guid
10. select
11. foreign
     In the above generators list, are used for int,long,short types of primary keys, and uuid, guid are used when the primary key column type is String type (varchar2).

1. sequence (org.hibernate.id.SequenceGenerator)
* This generator class is database dependent it means, we cannot use this generator class for all the database, we should know whether the database supports sequence or not before we are working with it

* Not has the support with MySql

* Here we write a sequence and it should be configured in HBM file and while persisting the object in the database, sequence is going to generate the identifier and it will assign to the Id property of persistent object, Then it will store ' the persistent object into Database.

* While inserting a new record in a database, hibernate gets next value from the sequence under assigns that value for the new record
Note
* MYSQL even won't allow to create SEQUENCE Object in that database.

* When we configured generator class for an entity, then we no need to assign identifier value to entity object while saving the entity. Even we assign the identifier value to entity, it will not consider our assigned value, it will use generator class generated value as identifier value. It applies to all generator classes.
Steps to work with "sequence" generator
Step 1: Create a sequence
SQL> CREATE SEQUENCE ACCNO-SEQ START WITH 1000 INCREMENT BY 1
To get next value
SQL> SELECT ACCNO-SEQ.NEXTVAL FROM DUAL;
To get current value
SQL> SELECT ACCNO-SEQ-CURRVAL FROM DUAL;

Step 2: configure the sequence in hbm file.
<hibernate-mapping> 
   <class ...> 
      <id ...> 
         <generator class="sequence">
            <param name="sequence">ACCNO-SEQ</param>
         </generator> 
      </id> 
       .....     
   </class> 
</hibernate-mapping> 
OR
<id name=" accountId " column="ACCNO">
   <generator class="org.hibernate.id.SequenceGenerator" > 
      <param name="sequence">ACCNO-SEQ </param>
   </generator>
</id>
OR
<id name=" accountId " column="ACCNO">
   <generator class="sequence"/>
</id>
Note: 
* If we don't configure any sequence name then it will take default sequence name(HIBERNATE-SEQUENCE ). But hibernate won't create the sequence, if already sequence is available with name "HIBERNATE-SEQUENCE", then it is

* I used by hibernate. Otherwise hibernate raises the exception. But remember, if we enable hbm2ddl.auto property in hibernate configuration file, then hibernate will create the database objects if they are not exist.

* It is not advisable to use the default sequence, always prefer to create a different sequence to each entity separately.

Step 3: Create the entity and save it without assigning identifier. 
Session session = SessionUtil.getSession();
session.getTransaction().begin();
Account account = new Account();
account.setName("Ashok");
account.setBalance(5000);
Serializable id = session.save(account) ;
System.out.println("Account is created with accno : "+id) ;
session.getTransaction().commit() ;
Internal Code
public class SequenceGenerator implements PersistentldentifierGenerator, Configurable {
public static final String SEQUENCE = "sequence";
.....................................................
.....................................................
public void configure(...){
sequenceName = PropertiesHelper.getString(SEQUENCE, params,"hibernate-sequence"); // default sequence name
parameters = params.getProperty(PARAMETERS);
}
public Serializable generate(. .. ) {
Preparedstatement st =. . ..prepareSelectStatement(sql);
Resultset rs = st.executeQuery();
rs.next();
Serializable result = . . iterate results ...
return result;
}
}
Note: Now onwards for the following generator classes I just give the HBM configuration, you can u;e the same entity saving logic(which we used in the above example as part of step-3) to test them.

2. assigned(org.hibernate.id.Assigned)
* This generator supports in all the databases

* This is the default generator class used by the hibernate, if we do not specify <generator/> element under <id/> element, then hibernate by default assumes it as "assigned" generator class.

* If generator class is assigned, then the programmer is responsible for assigning the identifier value to entity before saving into the database
HBM
<id name=" accountId " column="ACCNO">
<generator class="assigned" />
</id >
OR
<id name=" accountId " column="ACCNO">
<generator class="org.hibernate.id.Assigned" />
</id >
OR
<id name=" accountId " column="ACCNO" />
Internal Code:
public class Assigned implements IdentifierGenerator,Configurable{
public Serializable generate( ... ){
final Serializable id = . . . get the developer given id.
if (id==null) {
throw new IdentifierGenerationException(
"ids for this class must be manually assigned before calling save(): " +entityName);
return id;
}
}
3 increment(org.hibernate.id.IncrementGenerator) 
* This generator supports in all the databases, so it is database independent generator class.

* This generator is used for generating the id value for the new record by using the formula Max of id value in Database + 1

* If there is no record initially in the database, then for the first time this will saves primary key value as 1.
HBM
<id name="accountld" column="ACCNO">
<generator class="increment" />
</id>
OR
<id name=" accountld " column="ACCNO">
<generator class=" org.hibernate.id.IncrementGenerator " />
</id>
Internal Code
public class IncrementGenerator implements IdentifierGenerator, Configurable {
if (sql!=null) {
getNext(session);
}
return ...fi nal number after adding '1'
}
public void configure(. . .) {
..............
..............
..............
sql = "select max(" + column + ") from " + buf.toString();
}
private void getNext( Sessionlmplementor session ) {
Preparedstatement st = ....prepareSelectStatement(sql);
Resultset rs = st.executeQuery();
if ( rs.next() ) {
next = rs.getLong(1) + 1;
}
}
}

The HiLo Algorithm 
     The HiLo (High/Low) algorithm  knows how to generate unique number series using two values: the high and the low.  The high value is used as a base for a series (or range) of numbers, while the size of this series is donated by the low value. A unique series is generated using the following steps
1. Load the high value and atomically increment the high value

2. Multiple the high value by the low value (max*low), the result is the first number (lower bound) of the current series

3. The last number (higher bound) of the current series is donated by the following calculation: (max*low)+low-1

4. When a client needs to obtain a number the next one from the current is used, once the entire series has been / exhausted the algorithm goes back to step 1
Example
     Suppose that the current high value in the database is 52 and the low value is configured to be 32,767. When the algorithm starts is loads the high value from the database and increments it in the same transaction (the new high value in the database is now 53). The range of the current numbers series can now be calculated:
Lower bound = 52*32767 = 1,703,884
Upper bounds = 1,703,884+32,767-1= 1,736,650

     All of the numbers in the range of 1,703,884 to 1,736,650 can be safely allocated to clients, once this keys pool has been 1 exhausted the algorithm needs to access the database again to allocate a new keys pool. This time the high value is 53 (immediately incremented to 54) and the keys range is:
Lower bound = 53*32,767 = 1,736,651
Upper bounds = 1,736,651+32,767-1 = 1,769,417
And so on

     The big advantage of this algorithm is keys preallocation which can dramatically improve performance. Based on the low value we can control the database hit ratio. As illustrated using the 32,767 we hit the database only once in a 32,767, ' generated keys. The downside (at least by some people - but in my opinion this is a none-issue) is that each time the algorithm restarts it leaves a 'hole' in the keys sequence. Hibernate has several HiLo based generators:
1. TableHiLoGenerator
2. MuItipleHiLoPerTableGecierator
3. SequenceHiLoGenerator

TableHiLoGenerator
     A simple HiLo generator, uses a table to store the HiLo high value. The generator accepts the following parameters
* table -the table name, defaults to 'hibernate-unique-key'

* column -the name of the column to store the next high value, defaults to 'next-hi'

* max-low - the low number (the range) defaults to 32,767 (Short.MAX-VALUE)

MultipleHiLoPerTableGenerator
     A table HiLogenerator which can store multiple key sets (multiple high values each for a different entity). This is useful when we need each entity (or some of the entities) has its own keys range. It supportsthe following parameters:
* table - the table name, default to 'hibernate-sequences'

* primary-key-column - key column name, defaults to 'sequence-name'

* value-column - the name of the column to store the next high value, defaults to 'sequence-next-hi-value'

* primary-key-value - key value for the current entity (or current keys set), default to the entity's primary table name

* primary-key-length - length of the key column in DB represented as a varchar, defaults to 255

* max-low - the low numer (the range) defaults to 32,767 (Short.MAX-VALUE)
     The generator uses a single table to store multiple high values (multiple series), when having multiple entities using the same generator Hibernate matches an entity to a high value using the primary-key-value which is usually the entity name.

SequenceHiLoGenerator
     A simple HiLo generator but instead of a table uses a sequence as the high value provider.
* sequence - the sequence name, defaults to 'hibernate-sequence'

* max-low - the low number (the range) defaults to 9.

4. hilo (orghibernate.id.TableHiLoGenerator)
* This generator is database independent

* hilo uses a HiLo alogorithm to generate identifiers.

* Hilo algorithm generate identifiers based on the given table and column(stores high value). Default table is 'hibernate-unique-key' column is 'next-hi'.
<id name="accountId" column="ACCNO">
  <generator class="hilo" >
    <param name="table">HIGH-VAL-TAB</param>
    <param name="column">HIGH-VAL-COL</param>
    <param ndme="max_lo">60</param>
  </generator>
</id>
OR
<id name="accountId" column="ACCNO">
  <generator class="org.hibernate.id.TableHiLoGenerator" >
    <param name="table">HIGH-VAL-TAB</param>
    <param name="column">HIGH-VAL-COL</param>
    <param ndme="max_lo">60</param>
  </generator>
</id>
Internal code 
class TableHiLoGenerator extends TableGenerator {
.................
}
class TableGenerator {
    public static final String COLUMN = "column";
    public static final String DEFAULT-COLUMN-NAME = "next-hi";
    public static final String TABLE = "table";
    public static final String DEFAULT-TABLE-NAME = "hibernate-unique-key";
    .........................
    .........................
}
5. seqhilo(org.hibernate.id.SequenceHiLoGenerator)
     It is just like hilo generator class, But hilo generator stores its high value in table, where as seqhilo generator stores its high value in sequence.
HBM
<id name="accountId" column="ACCNO">
  <generator class="seqhilo" >
    <param name="sequence">ACCNO-SEQ</param>
    <param name="max-lo">5</param>
  </generator>
</id>
OR
<id name=" accountId " column="ACCNO">
  <generator class="seqhilo" >
    <param name="org.hibernate.id.SequenceHiLoGenerator ">ACCNO-SEQ</param>
    <param name="max-lo">5</param>
  </generator>
</id>
6. identity(org.hibernate.id.IdentityGenerator)
* This is database dependent, actually it's not working in oracle.

* Identity columns are support by DB2, MYSQL, SQL SERVER, SYBASE and HYPERSYNCSQL databases.

* This identity generator doesn't needs any parameters to pass

* Syntax to create identity columns in MYSQL database:
CREATE TABLE STUDENT(
SNO INT(10) NOT NULL AUTO-INCREMENT,
COURSE CHAR(2O),
FEE FLOAT,
NAME CHAR(20),
PRIMARY KEY (SNO)
)
HBM
<id name="accountId" column="ACCNO">
  <generator class="identity" >
  </generator>
</id>
7. native
* native is not having any generator class because, it uses internally identity or sequence or hilo generator classes.

* native picks up identity or sequence or hilo generator class depending upon the capabilities of the underlying database.
HBM
<id name="accountId" column="ACCNO">
  <generator class="hilo" >
    <param ndme="sequence">ACCNO_SEQ</param>
    <param name="table">HIGH-VAL-TAB</param>
    <param name="column">HIGH-VAL-COL</param>
    <param ndme="max_lo">60</param>
  </generator>
</id>
8. uuid(org.hibernate.id.UUIDHexGenerator
* uuid uses a 128-bit uuid algorithm to generate identifiers of type string.

* uuid generated identifier is unique with in a network.

* uuid algorithm generates identifier using IP address.

* uuid algorithm encodes identifier as a string(hexadecimal digits) of length 32.

* Generally uuid is used to generate passwords.
HBM
<id column="ACCNO" name="accountId">
  <generator class="uuid">
  </generator>
</id>
OR
<br />
<id column="ACCNO" name=" accountId ">
  <generator class="org.hibernate.id.UUIDHexGenerator">
  </generator>
</id>
User Defined Generator class
When we feel the existing generator classes are not fit for our requirement, then we will go for user defined generator class.

Steps to implement user defined generator class.
Step 1: Take any java class and implement org.hibernate.id .IdentifierGenerator and override generate() method. In this method implement the identifier generation logic as per the requirement.
Step 2: Give the generator class entry in the tag of HBM file.

Next Tutorial  Hibernate Query Language (HQL)

Previous Tutorial  Hibernate Batch Processing

No comments:

Post a Comment