Home/Learn/Hibernate & JPA/Batch Inserts & Updates

Batch Inserts & Updates

Advanced
Performance

Enable hibernate.jdbc.batch_size and use SEQUENCE ID generation (not IDENTITY) to allow Hibernate to batch INSERT/UPDATE statements, drastically reducing round-trips.

Overview

By default, Hibernate sends one INSERT/UPDATE statement per entity to the database — even when persisting a thousand records in a loop. This means 1000 individual round-trips for 1000 entities. **JDBC batching** groups multiple statements into a single network round-trip, dramatically reducing latency. Enable it with `hibernate.jdbc.batch_size`. Critical prerequisite: **use `SEQUENCE` or `TABLE` ID generation** — NOT `IDENTITY` (auto-increment). Hibernate cannot batch INSERTs with `IDENTITY` because it needs the generated ID from the DB after each INSERT to populate the entity. Optionally combine with `saveAll()` (Spring Data) and periodic `flush() + clear()` to avoid `OutOfMemoryError` on very large datasets.

Enabling JDBC Batching

Set `hibernate.jdbc.batch_size` in application.properties. For ordered batching (all INSERTs before all UPDATEs, improving cache efficiency), enable `hibernate.order_inserts` and `hibernate.order_updates`. Must use `SEQUENCE` ID generation — `IDENTITY` silently disables batching.

Spring Boot — enable JDBC batching with SEQUENCE
# application.properties
spring.jpa.properties.hibernate.jdbc.batch_size=50        # batch up to 50 statements
spring.jpa.properties.hibernate.order_inserts=true        # group all INSERTs together
spring.jpa.properties.hibernate.order_updates=true        # group all UPDATEs together
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true  # batch versioned (optimistic lock) entities

# Required: use SEQUENCE (not IDENTITY) for the ID strategy
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator = "product_seq")
    @SequenceGenerator(name = "product_seq",
                       sequenceName = "product_id_seq",
                       allocationSize = 50)   // pre-allocate 50 IDs per call — efficient
    Long id;
    // ...
}

# Why IDENTITY breaks batching:
# INSERT INTO product ... → DB returns generated ID
# Hibernate needs the ID immediately to set entity.id → cannot batch

Batch Insert Loop with Flush and Clear

When persisting large volumes, the persistence context grows unboundedly — all managed entities are held in memory. Periodically flush and clear the persistence context to keep memory constant. `flush()` sends the current batch to the DB; `clear()` detaches all entities from the session.

Hibernate — batch persist with flush+clear loop
@Transactional
public void bulkImport(List<ProductCsvRow> rows) {
    int batchSize = 50;   // must match hibernate.jdbc.batch_size

    for (int i = 0; i < rows.size(); i++) {
        Product product = mapper.toEntity(rows.get(i));
        entityManager.persist(product);

        if (i > 0 && i % batchSize == 0) {
            entityManager.flush();   // send the batch of 50 to DB
            entityManager.clear();   // detach all — free memory
        }
    }
    // final flush for remaining entities
    entityManager.flush();
    entityManager.clear();
}

// Spring Data equivalent: use saveAll() which calls persist() in a loop
// + configure batch_size → Spring Data does NOT auto-flush mid-loop
// so only practical for < few thousand rows without manual flush/clear

Verifying Batching with Statistics

Enable Hibernate statistics and the JDBC batch logging to verify batching is actually happening. Without `order_inserts`, mixed INSERT/UPDATE statements in the same session prevent batching. The log should show `"batch update returned unexpected row count from update [0]; actual row count: X"` if something went wrong, or the statistics counter `getPrepareStatementCount()` should be much lower than the entity count.

Hibernate — verify batching with statistics and logging
# Verify batching is active
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.engine.jdbc.batch.internal.BatchingBatch=DEBUG
logging.level.org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl=TRACE

# Log output with batching ON:
# DEBUG: Executing batch size: 50
# DEBUG: Executing batch size: 50
# → 1000 entities = 20 SQL calls (rounds of 50)

# Log output with batching OFF (IDENTITY strategy):
# DEBUG: Executing batch size: 1  (repeated 1000 times)

@Autowired SessionFactory sessionFactory;

@Test
void shouldBatchInserts() {
    Statistics stats = sessionFactory.getStatistics();
    stats.setStatisticsEnabled(true);
    stats.clear();

    importService.bulkImport(generate(500));

    // With batch_size=50: ~10 prepare statements, not 500
    assertThat(stats.getPrepareStatementCount()).isLessThan(20);
}

Key Points to Remember

  • 1hibernate.jdbc.batch_size groups multiple INSERT/UPDATE into one network round-trip
  • 2IDENTITY (auto-increment) ID generation silently DISABLES batching — use SEQUENCE instead
  • 3allocationSize on @SequenceGenerator pre-allocates IDs in bulk — reduces DB sequence calls
  • 4order_inserts + order_updates groups statements by type for better batch efficiency
  • 5Flush + clear every N entities to keep memory constant during large bulk imports
  • 6Verify batching with statistics: getPrepareStatementCount() should be rows/batch_size, not rows

Interview Questions

Sign in to ask Aria
1

Why does using GenerationType.IDENTITY prevent Hibernate from batching INSERT statements?

HardThoughtWorks
2

What does hibernate.order_inserts do and why is it important for batching?

MediumOracle
3

How would you bulk-import 100,000 records using Hibernate without running out of memory?

HardAmazon
4

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

MediumAtlassian
5

How would you verify that JDBC batching is actually working in a Spring Boot application?

MediumBooking.com

Ask Aria about Batch Inserts & Updates

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…