Card 03/ 07

GotchaDifficulty: Advanced1 min

When persist() Inserts: IDENTITY vs SEQUENCE Ids

With an auto-increment id the INSERT runs at persist, not at flush, whatever the usual rule says.

The usual rule is that JPA delays an entity's INSERT until the next flush, the moment pending changes are sent to the database. That holds for ids from a database sequence, and not for ids from an auto-increment column: with GenerationType.IDENTITY, persist() runs the INSERT immediately.

Two entities differ only in how their id is generated, and the same calling code persists each and then flushes:

java
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;   // on IdentityThing

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
Long id;   // on SequenceThing
text
IDENTITY: statements right after persist(), before flush: 1
IDENTITY: statements after flush(): 1

SEQUENCE: statements right after persist(), before flush: 1
SEQUENCE: statements after flush(): 2
run in a container, real Hibernate statistics: identical calling code, different id strategy

With IDENTITY, the database assigns the id in an auto-increment column, and there is no way to know it without running the INSERT. So persist() runs it at once, and flush() finds nothing left to do. With SEQUENCE, Hibernate can ask a separate database sequence for the next id first, which is the one statement before the flush, and delay the row's INSERT until the flush, as the usual rule says.

It matters when you rely on the timing: an INSERT that fails a database constraint fails at persist() with IDENTITY, and only at the flush with SEQUENCE. It also stops Hibernate from grouping inserts into batches under IDENTITY. So when timing or batching matters, check the id strategy first.