Home/Learn/Hibernate & JPA/@GeneratedValue Strategies

@GeneratedValue Strategies

Beginner
Fundamentals

AUTO delegates to the provider; IDENTITY uses DB auto-increment; SEQUENCE uses a DB sequence (most efficient for batch inserts); TABLE uses a dedicated ID table (avoid in production).

Overview

@GeneratedValue tells JPA how the primary key value should be generated. Four strategies: AUTO (provider-chosen, default), IDENTITY (DB auto-increment — prevents JDBC batch inserts), SEQUENCE (DB sequence with configurable allocation size — best for batch inserts), TABLE (sequence table — portable but slow and lock-prone — avoid in production). For MySQL with InnoDB, IDENTITY is common but SEQUENCE via a sequence emulation table is better for bulk operations. PostgreSQL natively supports sequences.

IDENTITY vs SEQUENCE

IDENTITY relies on DB auto-increment. Hibernate must execute the INSERT and immediately SELECT the generated key — this prevents batching. SEQUENCE pre-fetches ID blocks (allocationSize) from a DB sequence, enabling batched INSERTs.

Java — IDENTITY vs SEQUENCE strategies
// IDENTITY — simple, works with MySQL AUTO_INCREMENT
// ✗ Disables JDBC batch inserts (Hibernate needs each insert's ID immediately)
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

// SEQUENCE — pre-fetches ID blocks; enables batch inserts
// Works with PostgreSQL natively; MySQL needs a workaround sequence table
@Entity
@SequenceGenerator(
    name            = "order_seq",
    sequenceName    = "order_id_seq",   // DB sequence name
    allocationSize  = 50                // pre-allocate 50 IDs per round-trip
)
public class Order {
    @Id
    @GeneratedValue(
        strategy  = GenerationType.SEQUENCE,
        generator = "order_seq"
    )
    private Long id;
}

// With allocationSize=50:
// 1st ID fetch: SELECT nextval('order_id_seq') → 1
// Hibernate caches IDs 1-50 in memory — no DB call for the next 49 entities
// 51st entity: SELECT nextval → 51, caches 51-100, etc.

MySQL-Specific: Emulating Sequences

MySQL does not have native sequences (until 8.x with sequences plugin). Emulate them with a sequence table or use the hi-lo algorithm with a table generator. For most applications, IDENTITY is acceptable; for bulk inserts, use dedicated ID generation service or UUID.

Java — TABLE strategy and UUID generation
// Option 1 — TABLE strategy (portable but slow, lock-prone — avoid)
@Entity
@TableGenerator(
    name          = "order_tg",
    table         = "id_generator",
    pkColumnName  = "gen_name",
    valueColumnName = "gen_val",
    pkColumnValue = "order_id",
    allocationSize = 50
)
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "order_tg")
    private Long id;
}

// Option 2 — UUID (no sequence needed — good for distributed systems)
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)    // JPA 3.1 / Hibernate 6
    @UuidGenerator(style = UuidGenerator.Style.TIME)   // Hibernate 6 — time-ordered
    private UUID id;
}

// UUIDv7 (time-ordered) avoids random page splits in B-tree indexes
// Use org.hibernate.annotations.UuidGenerator for version control

Bulk Insert Performance

IDENTITY strategy disables JDBC batching. To batch INSERT 10,000 rows efficiently, use SEQUENCE with allocationSize or manage IDs manually with a pre-assigned ID list.

Properties + Java — bulk insert with SEQUENCE
// Batch insert — SEQUENCE strategy enables proper JDBC batching

# application.properties
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.generate_statistics=true

@Service
@Transactional
public class BulkOrderService {

    public void importOrders(List<CreateOrderDTO> dtos) {
        int batchSize = 50;
        for (int i = 0; i < dtos.size(); i++) {
            Order order = new Order(dtos.get(i));
            em.persist(order);

            if (i % batchSize == 0 && i > 0) {
                em.flush();    // flush batch to DB
                em.clear();    // detach all entities — free memory
            }
        }
    }
}

// With SEQUENCE + allocationSize=50 + jdbc.batch_size=50:
// Hibernate pre-fetches 50 IDs → builds 50-row INSERT batch → single DB round-trip
// With IDENTITY: 1 INSERT per row = 10,000 round-trips

Key Points to Remember

  • 1IDENTITY uses DB auto-increment — simple but disables JDBC batch INSERT.
  • 2SEQUENCE pre-fetches ID blocks (allocationSize) — enables batching; use for bulk inserts.
  • 3TABLE strategy is portable but uses DB row locks — avoid in production.
  • 4UUID (@GeneratedValue(strategy=UUID)) is useful for distributed systems with no sequence.
  • 5UUIDv7 (time-ordered) avoids random B-tree index splits unlike random UUIDv4.
  • 6For bulk inserts: flush + clear every N entities to avoid OutOfMemoryError from L1 cache growth.

Interview Questions

Sign in to ask Aria
1

Why does GenerationType.IDENTITY prevent JDBC batch inserts?

HardNetflix
2

What is allocationSize in @SequenceGenerator and how does it improve performance?

MediumAmazon
3

What is the difference between IDENTITY and SEQUENCE generation strategies?

MediumInfosys
4

When would you use UUID as a primary key instead of a numeric sequence?

MediumUber
5

How do you efficiently insert 100,000 entities using JPA?

HardFlipkart

Ask Aria about @GeneratedValue Strategies

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…