Your application requires the insertion of thousands of records into the database at once. How do you optimize this batch process using Spring JPA?

Batch inserting thousands of records efficiently means grouping many INSERT statements into fewer database round trips instead of sending one statement per row. In Spring JPA this is done by enabling Hibernate's JDBC batching and following a few practices that let Hibernate actually take advantage of it.

Key Points: • Setting spring.jpa.properties.hibernate.jdbc.batch_size in application.properties tells Hibernate how many statements to group per batch. • spring.jpa.properties.hibernate.order_inserts=true and order_updates=true help Hibernate group same-table statements together for more effective batching. • Using GenerationType.IDENTITY for primary keys defeats batching in Hibernate, since it forces an insert per row to get the generated key; SEQUENCE-based generation batches better. • Periodically calling entityManager.flush() and entityManager.clear() during a large loop prevents the persistence context from growing unbounded and consuming excessive memory. • Running the whole batch inside a single transaction reduces per-statement transaction overhead compared to committing after every row.

Example: Importing 50,000 customer records from a CSV file in a loop, flushing and clearing the persistence context every 50 records with batching enabled, dramatically reduces both database round trips and memory usage compared to calling save() individually with auto-commit per row.

Code 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
@Transactional
public void importCustomers(List<Customer> customers) {
    for (int i = 0; i < customers.size(); i++) {
        entityManager.persist(customers.get(i));
        if (i % 50 == 0 && i > 0) {
            entityManager.flush();
            entityManager.clear();
        }
    }
}

Interview Tip: A concise interview answer is:

"I enable Hibernate's JDBC batching with hibernate.jdbc.batch_size, use a sequence-based ID generator instead of IDENTITY since IDENTITY forces per-row inserts, and periodically flush and clear the persistence context inside the loop to keep memory bounded. That combination turns thousands of individual INSERT round trips into a much smaller number of batched statements."