Hibernate @GeneratedValue

I spent a bit of time figuring out what all the annotations for a Hibernate/JPA @Id field should be, and wanted to document it.

First up, with any entity, you need to add an @Id annotation to a field, I typically use a Long field.

package test.model;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name="test_table")
public class TestTable implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="test_table_id")
    private Long id;

    // getters & setters
}

All is good and well, until we want to persist new TestTable objects. Now we need a @GeneratedValue annotation… you can use a variety of strategies: GenerationType.AUTO, IDENTITY, SEQUENCE, or TABLE. Auto will have Hibernate guess at which version is best for your database type (e.g., a sequence for Oracle, an identity for MS SQL).

One requirement I typically have is that the application code and resultant data structure be database agnostic, and more specifically, have the same index strategy despite different databases. So, we can define a strategy that will be implemented the same regardless of database type.

For a TABLE generation strategy, you can simply specify the strategy. This will result in a table “hibernate_sequences” and a caching size of 10 (I think).

@Id
@Column(name="test_table_id")
@GeneratedValue(strategy=GenerationType.TABLE)
private Long id;

This is fine, but Hibernate is actually caching the index number for you, and only writing out the index to table every 10 or so inserts. We will want to change that (despite the performance hit) if we have anything that may not be using Hibernate to access these indexes.

The following will do the same as above (hibernate_sequences table), but flush the index out on every increment of the index. The “generatorName” below is just an arbitrary name that pairs the two annotations.

@Id
@Column(name="test_table_id")
@GeneratedValue(strategy=GenerationType.TABLE, generator="generatorName")
@TableGenerator(name="generatorName", allocationSize=1)
private Long id;

Want more control? It’s available… Here we’ll specify the table to put the sequences into, in case there was an existing table strategy used in some other project that you’d like to work with.

@Id
@Column(name="test_table_id")
@GeneratedValue(strategy=GenerationType.TABLE, generator="generatorName")
@TableGenerator(name="generatorName", table="TableID",
				pkColumnName="tablename", // TableID.TableName (value = table_name, test_table, etc.)
				valueColumnName="id", // TableID.ID (value = 1,2,3,etc.)
				allocationSize=1 // flush every 1 insert
)
private Long id;

We can also use a custom coded IdentifierGenerator when using Hibernate. The class MyIdGenerator extends IdentifierGenerator in the example below. I also chose to implement Configurable so I could pass the table name into the generator.

// new imports
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
// annotations for ID field
@Id
@Column(name="test_table_id")
@GeneratedValue(generator="UniqueIdGenerator")
@GenericGenerator(name="UniqueIdGenerator", strategy="test.util.MyIdGenerator",
	parameters = { @Parameter(name="tableName", value="test_table") } )
private Long id;

And here is the ID generator…

package test.util;

import java.io.Serializable;
import java.sql.Connection;
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.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.type.Type;

public class MyIdGenerator implements IdentifierGenerator, Configurable {
	private String tableName;
	
    public void configure( Type arg0, Properties props, Dialect arg2 )
            throws MappingException {
        setTableName( props.getProperty( "tableName" ) );
    }

    public Serializable generate( SessionImplementor session, Object arg1 )
            throws HibernateException {
        // we want to open a new connection / transaction
        Connection conn = session.getBatcher().openConnection();

        Serializable id = null;
        try {
            id = new IdGenerator().generate( conn, tableName );
        } catch( SQLException e ) {
            throw new HibernateException( e );
        } finally {
            session.getBatcher().closeConnection( conn );
        }
        return id;
    }

    // getter & setter for the configurable parameters
    public String getTableName() {
        return tableName;
    }
    public void setTableName( String tableName ) {
    	this.tableName = tableName;
    }
}

4 comments

  1. Hei this is really cool.. I have searching for a way to use an already existing PL-SQL code for id generation. (Hibernate is being introduced for a legacy application which uses Pl-SQL for Id generation)

    Your post was of really helpful for me.. I could acheive what I want.. thanks a lot..

Comments are closed.