Batch inserts and bulk updates
Turn fifty round trips into one, and know what a bulk statement does to the context.
Open this lesson in the learning hubKey points
- Set
hibernate.jdbc.batch_size(30-50 is typical). Without it every INSERT is its own round trip, however tight your loop is. - Batching needs ids up front, so
IDENTITYsilently disables it. Use aSEQUENCEwith a matching allocationSize. - Add
order_insertsandorder_updatesso Hibernate groups statements by table instead of breaking every batch. - In a long loop,
flush()thenclear()every batch, or the context holds every entity until the heap runs out. - A JPQL
updateordeleteis one statement, but it bypasses the context: no cascades, no callbacks. - Call
em.clear()after a bulk statement, or the nextfind()returns the stale object from the first level cache.
Example
# --- application.properties -------------------------------------
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
# ids must not come from IDENTITY, or batching is off whatever you set
# --- the loop ----------------------------------------------------
# @Transactional
# public void importAll(List<Book> books) {
# for (int i = 0; i < books.size(); i++) {
# em.persist(books.get(i));
# if (i % 50 == 0) { // one batch out, one context emptied
# em.flush();
# em.clear();
# }
# }
# }
# --- one statement instead of N ----------------------------------
# int rows = em.createQuery("update Book b set b.archived = true where b.publishedOn < :cut")
# .setParameter("cut", cutoff)
# .executeUpdate();
# em.clear(); // loaded Books are now stale
Batch size plus a sequence turns an import from minutes into seconds.
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.