Primary Keys & @GeneratedValue
Understand how @GeneratedValue works in Hibernate and the differences between GenerationType.IDENTITY, SEQUENCE, and AUTO.
Introduction
So far, every example manually assigned an id before saving. In real applications, you almost always want the database (or Hibernate) to generate primary keys automatically. This lesson covers @GeneratedValue and the three most common generation strategies: IDENTITY, SEQUENCE, and AUTO.
- Why manually assigning primary keys does not scale.
- How @GeneratedValue works alongside @Id.
- The differences between IDENTITY, SEQUENCE, and AUTO.
- How to choose the right strategy for a given database.
Why Auto-Generate Primary Keys?
Manually assigning IDs, as earlier lessons did, requires the application to track the next available ID itself — a process that is error-prone and breaks down completely with multiple concurrent users. Auto-generation delegates that responsibility to the database or to Hibernate, which can guarantee uniqueness safely.
If two requests both try to manually assign id = 5 at the same time, one insert will fail (or worse, silently overwrite data) due to a primary key collision. Auto-generation avoids this entirely.
@GeneratedValue Basics
@GeneratedValue is paired with @Id and tells Hibernate how to produce a value for the primary key automatically when a new entity is saved.
import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;With this in place, you no longer call student.setId(...) at all — Hibernate populates it after the insert.
GenerationType.IDENTITY
IDENTITY relies on the database's own auto-increment column feature (like MySQL's AUTO_INCREMENT). The database assigns the next value at insert time, and Hibernate reads it back immediately afterward.
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;Click Run to see what this code prints.
IDENTITY requires the insert to happen immediately to retrieve the generated key, which disables Hibernate's JDBC batch inserts for that entity — a real performance consideration for high-volume inserts.
GenerationType.SEQUENCE
SEQUENCE uses a separate database sequence object (supported by PostgreSQL, Oracle, and others — not natively by MySQL) to pre-allocate primary key values. Hibernate can request a batch of values ahead of time, which allows batching inserts efficiently.
import jakarta.persistence.*;
@Id@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "student_seq")@SequenceGenerator(name = "student_seq", sequenceName = "student_sequence", allocationSize = 1)private Long id;Click Run to see what this code prints.
Because the ID is obtained from the sequence before the insert runs, Hibernate can batch multiple inserts together efficiently — a key advantage over IDENTITY for bulk operations.
GenerationType.AUTO
AUTO lets Hibernate pick an appropriate strategy automatically, based on the configured dialect for the target database. It is a convenient default when you do not need to control the exact mechanism.
@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;AUTO is a reasonable default for prototyping or when database portability matters more than fine control, but explicit strategies (IDENTITY or SEQUENCE) give you more predictable, tunable behavior in production systems.
Choosing a Strategy
| Strategy | Best For | Trade-off |
|---|---|---|
| IDENTITY | MySQL and databases with native auto-increment support. | Disables JDBC insert batching for that entity. |
| SEQUENCE | PostgreSQL, Oracle, and databases with native sequence support. | Requires an extra round trip unless allocationSize batching is tuned. |
| AUTO | Prototypes or database-agnostic code. | Less explicit and predictable than choosing a strategy directly. |
Common Mistakes
- Using GenerationType.SEQUENCE with a database that has no native sequence support, like MySQL.
- Manually setting an id value on an entity that also uses @GeneratedValue, causing confusing conflicts.
- Assuming IDENTITY and SEQUENCE perform identically at scale — their batching behavior differs significantly.
- Forgetting that AUTO's actual behavior depends entirely on the configured dialect.
Best Practices
- Match the generation strategy to your actual database — IDENTITY for MySQL, SEQUENCE for PostgreSQL/Oracle.
- For high-volume insert workloads, prefer SEQUENCE where supported, to take advantage of batching.
- Never manually assign an id on an entity configured with @GeneratedValue.
- Tune allocationSize on @SequenceGenerator deliberately rather than leaving it at its default for high-throughput systems.
Frequently Asked Questions
GenerationType.IDENTITY is the natural fit, since MySQL supports AUTO_INCREMENT natively and does not support true database sequences.
Hibernate needs the generated key immediately after each insert to keep track of the entity, which forces each insert to execute right away rather than being grouped into a batch.
It is a reasonable starting point, especially for prototypes, but production systems generally benefit from explicitly choosing IDENTITY or SEQUENCE based on the target database.
Yes, though the generation strategies differ — UUID-based keys often use a custom generator rather than IDENTITY or SEQUENCE.
Key Takeaways
- @GeneratedValue automates primary key assignment, avoiding manual ID management.
- IDENTITY relies on the database's native auto-increment feature but disables insert batching.
- SEQUENCE uses a database sequence and supports efficient batched inserts.
- AUTO lets Hibernate choose a strategy based on the configured dialect.
Summary
Choosing the right primary key generation strategy is a small decision with real performance implications at scale. With entities, mapping annotations, and primary key generation covered, you are ready to move from single saves into full CRUD operations with Hibernate.