Pages

Wednesday, December 29, 2010

Hibernate Id Generation and Oracle Sequences

I thought that @SequenceGenerator allocationSize attribute defined the number to increment the sequence. The API says that anyway... If you forget the fact that, you can't increment the Oracle Sequences more or less than the number you specified while you created them, you may find your self in a trap.
By default you create your sequences with an increment of 1. This becomes a problem if your app. does bulk inserts. Every insert will need a select to the sequence to generate an id which in turn will hinder the performance of app.
One of the ways Hibernate tries to overcome this problem is using a simple strategy called HiLo. Basically Hibernate uses the allocationSize attribute as a multiplier to sequence value. If the sequence is 'x' Hibernate uses id's from '(x-1)*allocationSize' to 'x*allocationSize' to following inserts after a single select and an increment to the sequence.
While I think this is a feasible approach, down side is all the apps. using this sequence will have to use this algorithm to generate the id's, dealing with the fixed allocationSize, or the generated id's may collide with each other.
Instead of using sequences, using a table which stores id's is much better since you can define the increment size to the amount that you actually require. Using the following annotation and the optimizer, causes a simpler and similar behavior that API actually states. Here is the annotation;
1:    @GeneratedValue(strategy = GenerationType.TABLE, generator = "genFooTable")
2: @GenericGenerator(name = "genFooTable",
3: strategy = "org.hibernate.id.enhanced.TableGenerator",
4: parameters = {
5: @Parameter(name = "table_name", value = "SEQUENCES"),
6: @Parameter(name = "value_column_name", value = "NEXTVALUE"),
7: @Parameter(name = "segment_column_name", value = "NAME"),
8: @Parameter(name = "segment_value", value = "genFooTable"),
9: @Parameter(name = "increment_size", value = "10"),
10: @Parameter(name = "optimizer", value = "org.mca.PooledIdOptimizer")
11: })
The optimizer :
1:  public class PooledIdOptimizer extends org.hibernate.id.enhanced.OptimizerFactory.OptimizerSupport {
2: private long value;
3: private long hiValue = -1;
4: public synchronized Serializable generate(AccessCallback callback) {
5: if ((hiValue < 0) || (value >= hiValue)) {
6: value = callback.getNextValue();
7: hiValue = value + incrementSize;
8: }
9: return make(value++);
10: }
I made a little editing with the Hibernate's original pooled optimizer since I didn't like the way it work.
Cheers!

Monday, December 27, 2010