Why GenerationType.IDENTITY disables batch inserts
The id strategy quietly decides whether Hibernate can batch at all.
Open this lesson in the learning hubKey points
IDENTITYmeans the database assigns the key on insert. Hibernate needs that value immediately to manage the entity, so it must execute each insert on its own and read the key back.- That makes insert batching impossible with IDENTITY, no matter what
hibernate.jdbc.batch_sizeis set to. Setting the property and seeing no improvement is the usual way people discover this. SEQUENCEis different: Hibernate can ask the sequence for values up front, so it knows every id before inserting and can send one batched statement.- A pooled optimiser makes that cheap - one sequence call reserves a block of fifty ids, so a thousand inserts cost twenty sequence round trips instead of a thousand.
- MySQL historically pushed people to IDENTITY because it had no sequences; MySQL 8 still has none, so a table-based generator is the alternative there. On PostgreSQL and Oracle, SEQUENCE should be the default.
- Ordering matters too.
order_insertsandorder_updatesgroup statements by table so they can actually be batched, rather than alternating between tables and breaking every batch.
Example
// Blocks batching. Every insert is its own round trip.
@Entity
class OrderBad {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
// Batches properly, and reserves ids in blocks of 50.
@Entity
class Order {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "order_seq")
@SequenceGenerator(name = "order_seq", sequenceName = "order_seq",
allocationSize = 50) // MUST match the DB INCREMENT BY
private Long id;
}
// CREATE SEQUENCE order_seq START WITH 1 INCREMENT BY 50;
//
// allocationSize and INCREMENT BY must agree. If they disagree, Hibernate
// hands out ids the database will later reissue -> duplicate key errors
// that appear only under concurrency.
---
# Batching needs all four. The first alone does nothing with IDENTITY.
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true # group by table, or batches break
order_updates: true
batch_versioned_data: true
# And flush periodically, or the persistence context grows without bound
# and dirty checking gets slower with every entity added:
#
# for (int i = 0; i < rows.size(); i++) {
# em.persist(rows.get(i));
# if (i % 50 == 0) { em.flush(); em.clear(); }
# }
IDENTITY forces one insert per row - use SEQUENCE with a pooled allocation size if you want batching to work at all.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Hibernate course, and every lesson in it is listed on the Hibernate contents page.