Help

Introducing...

Starting in 3.2.3, I include 2 new identifier generators targetted at portability. They take a differect approach to portability than do the older native generator. Typically speaking using a synthetic identifier generation strategy while eyeing portability really comes down to wanting the capabilities that a sequence provides even though the database may not support sequences. Note that I explicitly leave off IDENTITY-style generators because generally speaking an Object/Relational Mapping technology will prefer identifier generation strategies where the identifier value can be retrieved before performing (and without having to actually perform) the insert statement; this is certainly true of Hibernate and other transactional write behind technologies because in the case of IDENTITY columns the insert must be performed immediately (and furthermore, because JDBC does not define a mechanism to retrieve batches of IDENTITY generated values, batching must be impliciitly disabled for entities using an IDENTITY generator), circumventing the transactional write behind behavior.

The two generators are:

  • org.hibernate.id.enhanced.SequenceStyleGenerator - the approach it takes to portability is that really you dont care whether you are physically using a SEQUENCE in the database; really you just want a sequence-like generation of values. On databases which support SEQUENCES, SequenceStyleGenerator will in fact use a SEQUNCE as the value generator; for those database which do not support SEQUENCES, it will instead use a single-row table as the value generator, but with the same exact charecteristics as a SEQUENCE value generator (namely it deals with the sequence table in a separate transaction at all times).
  • org.hibernate.id.enhanced.TableGenerator - while not specifically targetting portability, TableGenerator can certainly be used across all databases. It uses a multi-row table where the rows are keyed by a (configurable) sequence_name column; one approach would be to have each entity define a unique sequence_name value in the table to segment its identifier values. It grew out of the older org.hibernate.id.MultipleHiLoPerTableGenerator and uses basically the same table structure. However, while MultipleHiLoPerTableGenerator inherently applies a hi-lo algorithm to the value generation, this new TableGenerator was added to be able to take advantage of the pluggable optimizers.

Both generators, in addition to other specific parameters, share 3 useful configuration parameters:

  • optimizer
  • initial_value
  • increment_size

The role of the optimizer is to limit the number of times we actually need to hit the database in order to determine the next identifier value. The exact effect of initial_value and increment_size somewhat depend on the optimizer chosen. optimizer provides 3 choices:

  • none - says to hit the database on each and every request
  • hilo - says to use an in-memory pooling technique which is the same basic logic as the older Hibernate hilo or seqhilo generators. In terms of the database values, they are incremented one at a time; in other words, increment_size applies to the in-memory algorithm
  • pooled - says to use a stored pooling technique. Unlike hilo, where incremental values are stored and retrieved from the database sequence/table, pooled stores the actual current hi-value into the database. As an example, consider increment_size=10

Under the covers

So generally speaking, both the hilo and pooled optimizer seeks to optimize performance by minimizing the number of times we need to hit the database. Great! So then exactly how are they different? Well, lets take a look at the values stored in the database as a means to illustrate the distinction.

optimizer=hilo (increment_size=10)

After the initial request, we will have:

|  value (db)  |  value (in-memory)  |  hi (in-memory)  |
| 1            | 1                   | 11               |

The db-value and hi will remain the same until the 12th request, at which point we would clock over:

|  value (db)  |  value (in-memory)  |  hi (in-memory)  |
| 2            | 12                  | 21               |

Essentially, hi defines the clock-over value; once the in-memory value reaches the hi value, we need to hit the database and define a new bucket of values. The major drawback to this approach is when using this strategy with legacy applications that also need to insert values; those other applications must also understand and use this hilo algorithm.

optimizer=pooled (increment_size=10)

After the initial request, we will have:

|  value (db)  |  value (in-memory)  |  hi (in-memory)  |
| 11           | 1                   | 11               |

The db-value and hi will remain the same until the 12th request, at which point we would clock over:

|  value (db)  |  value (in-memory)  |  hi (in-memory)  |
| 21           | 12                  | 31               |

As you can see, with this optimizer the increment_size is actually encoded into the database values. This is perfect for databases which support sequences because typically they also define an /INCREMENT BY/ option to creating the sequence such that calls to get the next sequence value automatically apply the proper increment_size. Even if other applications are also inserting values, we'll be perfectly safe because the SEQUENCE itself will handle applying this increment_size. And in practice, it turns out, you will also be safe if SequenceStyleGenerator reverts to using a table in the same situation because of how the clock over happens.

Conclusion

I would expect that these two new generators actually replace currently existing ones in terms of short-hand names. Specifically, I would expect

  • the implementation behind sequence to change from org.hibernate.SequenceGenerator to the new org.hibernate.id.enhanced.SequenceStyleGenerator
  • the implementation behing table to change from org.hibernate.TableGenerator to the new org.hibernate.id.enhanced.TableGenerator

The second is the more risky replacement because of the big difference between the two. But we've all along discouraged direct use of the current table generator so I think we should be safe there. I am still uncertain when that replacement will happen (probably 4.0?), but in the meantime, the new generators are available and highly recommended for use.

12 comments:
 
09. Apr 2008, 04:19 CET | Link
Jeebus

The SequenceStyleGenerator with pooled optimizer seems like the answer to a PostgreSQL user's prayers... except... It still doesn't work with Postgres! It is still running the old hilo way with the ids being multiplied by the increment_size. The results is that the sequence immediately becomes totally out of sync. What gives? What have I missed?

BTW, is the 31 above a typo?

ReplyQuote
 
29. Apr 2008, 22:53 CET | Link
Tom M.

I too am questioning the value 31 used in the pooled example. It should be 21, right?

 
12. May 2008, 23:22 CET | Link
Roberto Melo Cavalcante | rmcavalcante(AT)gmail.com

Hi,

I have a question quite not directly related to the post, but still, I'm kind of desperate so here comes: How can I have access to another sequence but the POJO's Id? For exemple:

@Entity
@Table(name = "usuario", schema = "myschem")
@SequenceGenerator(name = "idUsuarioSequence", sequenceName = "usuario_id_usuario_seq")
public class Usuario implements java.io.Serializable {

	private Integer idUsuario;
	private Integer extraSequencedNumber;

	@Id
	@Column(name = "id_usuario", unique = true, nullable = false)
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "idUsuarioSequence")
	@NotNull
	public Integer getIdUsuario() {
		return this.idUsuario;
	}

	public void setIdUsuario(Integer idUsuario) {
		this.idUsuario = idUsuario;
	}


        public Integer getExtraSequencedNumber() {
		return this.extraSequencedNumber;
	}

	public void setExtraSequencedNumber(Integer extraSequencedNumber) {
		this.extraSequencedNumber = extraSequencedNumber;
	}
}

So consider de property extraSequencedNumber the other property my application requires to be bound to a database sequence. How can I achieve that without using HQL?

Thanks in advance.

 
09. Oct 2008, 16:30 CET | Link
Krasimir Chobantonov
You need to use " INCREMENT BY " when you are creating the sequence in PostgreSQL - the value should match the value that you will give to the Hibernate ID generator using the increment_size
 parameter.
 
13. Nov 2008, 12:57 CET | Link
Drew

How can you use these new generators using JPA annotations? Is that even possible? If not, then what's the alternative? How can you use these using Hibernate annotation?

Thanks,

Drew

 
16. Dec 2008, 21:46 CET | Link
Paul Williams | infokingpaul(AT)gmail.com

Really an interesting article. How can identifier generator help for standalone application

 
31. Jul 2009, 04:53 CET | Link
Mark

Is there anyway to use the Id Generator directly instead of just being able to use it with an Id column. Is there any way to directly instantiate the Id generator?

 
04. Aug 2009, 01:17 CET | Link
Mandy

Thanks, this feature is very useful and helpful. It seems this algorithm is not working as explained in the blog. If I give an increment size of 10 then when I get the first record then it puts 21 in the database instead of 11. When it reaches 20 then it inserts 31 as intended and from there on works fine. The main problem with the algorithm is that if I manually change the value in the database to 35 from 31 then I would think that it will skip ids from 31 to 34 but it doesn't rather it skips ids from 21 to 24.

 
22. Jan 2010, 22:16 CET | Link
sboisen

Beware that now, more than 2 years after the initial release, the pooling optimizer is fundamentally broken :HHH-3608

That is, it will not interoperate with other JVM instances or other external row producers, using the sequence to generate primary keys for the same table.

 
22. Feb 2010, 20:21 CET | Link
dcato

And hilo optimizer is broken much miserably : HHH-3628

16. Feb 2014, 12:41 CET | Link
Click HELP for text formatting instructions. Then edit this text and check the previ__.
 
02. Oct 2014, 13:16 CET | Link
jassica

Conveniently coming in one size fits most, this lingerie is made of vinyl and nylon so it is sure to fit almost all body types. Simply slink into this sexy number and prepare for a wild night! sparxxrx

Post Comment