Search results
Spring Data JPA, Hibernate, Batch Insert and Update Statements Configuration
- Ensure your JDBC driver supports batching and that it’s enabled in your database connection URL (e.g., for MySQL, add the
rewriteBatchedStatements=trueparameter).
application.yml:
spring:
datasource:
url: jdbc:postgresql://<host>:<port>/<db_name>?rewriteBatchedInserts=true
jpa:
properties:
hibernate:
jdbc:
batch_size: 25
batch_versioned_data: true # For @Version optimistic locking
order_inserts: true
order_updates: true
- Logging configuration for batching verification during development. Not recommended for production.
Recipe to configure logging for Spring Data JPA, Hibernate
Recipe to expose Hibernate Statistics through JMX MBeans in Spring Data JPA
application.yml:
logging:
level:
# org.hibernate.engine.jdbc.batch.internal.BatchingBatch # Hibernate 5.x
org.hibernate.orm.jdbc.batch: TRACE # Hibernate 6.x
org.hibernate.stat: DEBUG
spring:
jpa:
properties:
hibernate:
generate_statistics: true
- Avoid using
IDENTITYin JPA Entities’ primary keys meant to be inserted, either parent or children associations, because Hibernate must retrieve theIDimmediately after each insert.
Product.java:
@Entity
// ...
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_id_seq")
@SequenceGenerator(
name = "product_id_seq",
sequenceName = "product_id_sequence",
// Match allocationSize with batch_size from application.yml config for efficiency
// Match allocationSize with product_id_seq sequence's CACHE value
allocationSize = 25
)
private Long id;
// ...
}
schema.sql:
CREATE SEQUENCE product_id_seq
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 25; -- Make JPA Entity's @SequenceGenerator.allocationSize attribute match this value
Usage
- A Batch job to process active customers, update some attributes then disactivating them to prevent an infinite loop.
DefaultDvdRentalService.java:
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void processActiveCustomers() {
boolean stillPending = true;
while(stillPending) {
stillPending = this.rwTransactionTemplate.execute(txStatus -> {
Pageable page = PageRequest.of(0, 25);
List<Customer> customers = this.customerRepository.findAndLockTopNByActiveOrderByLastUpdateAsc(page, 1);
if (CollectionUtils.isEmpty(customers)) {
return false;
}
// Entities to process
customers.forEach((customer) -> {
// Implement some logic
// FIXME
// Update the attribute used in the select statement
// so that the loop doesn't become an infinite loop
customer.setActive(0);
customer.setActivebool(false);
});
return true;
});
}
}
- Verification logs.
...
2025-10-26T17:29:30.732-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Created Batch (25) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
2025-10-26T17:29:30.732-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Adding to JDBC batch (1) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
2025-10-26T17:29:30.732-04:00 DEBUG 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.SQL :
update
public.customer
set
active=?,
activebool=?,
address_id=?,
create_date=?,
email=?,
first_name=?,
last_name=?,
last_update=?,
store_id=?
where
customer_id=?
...
2025-10-26T17:29:30.679-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Adding to JDBC batch (2) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
2025-10-26T17:29:30.679-04:00 DEBUG 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.SQL :
update
public.customer
set
active=?,
activebool=?,
address_id=?,
create_date=?,
email=?,
first_name=?,
last_name=?,
last_update=?,
store_id=?
where
customer_id=?
...
2025-10-26T17:29:30.679-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Adding to JDBC batch (3) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
...
2025-10-26T17:29:30.699-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Adding to JDBC batch (25) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
...
2025-10-26T17:29:30.708-04:00 TRACE 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : Executing JDBC batch (25 / 25) - `com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
2025-10-26T17:29:30.713-04:00 DEBUG 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : PreparedStatementDetails did not contain PreparedStatement on #releaseStatements : update public.customer set active=?,activebool=?,address_id=?,create_date=?,email=?,first_name=?,last_name=?,last_update=?,store_id=? where customer_id=?
2025-10-26T17:29:30.717-04:00 DEBUG 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] org.hibernate.orm.jdbc.batch : PreparedStatementDetails did not contain PreparedStatement on #releaseStatements : update public.customer set active=?,activebool=?,address_id=?,create_date=?,email=?,first_name=?,last_name=?,last_update=?,store_id=? where customer_id=?
2025-10-26T17:29:30.721-04:00 INFO 31441 --- [spring-boot-scheduled-multiple-threads] [eduler-thread-1] i.StatisticalLoggingSessionEventListener : Session Metrics {
25163 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
87070 nanoseconds spent preparing 2 JDBC statements;
2232062 nanoseconds spent executing 1 JDBC statements;
4334130 nanoseconds spent executing 1 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
36783018 nanoseconds spent executing 1 flushes (flushing a total of 25 entities and 50 collections);
4743 nanoseconds spent executing 1 pre-partial-flushes;
2817 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
}
Relevant logs:
-
Executing JDBC batch (25 / 25) -com.asimiotech.demo.domain.dvdrental.Customer#UPDATE` -
87070 nanoseconds spent preparing 2 JDBC statements;
One for theselect ... fetch first ? rows only for no key update skip locked, another for theupdate public.customer set ... -
4334130 nanoseconds spent executing 1 JDBC batches;
References the UPDATE 25 rows statementExecuting JDBC batch (25 / 25) -com.asimiotech.demo.domain.dvdrental.Customer#UPDATE`
Also related to this page:
Recipe to configure logging for Spring Data JPA, Hibernate
Recipe to expose Hibernate Statistics through JMX MBeans in Spring Data JPA
Recipe to turn off autocommit for Spring Data JPA, Hibernate, Hikari RESOURCE_LOCAL JPA transactions
How to configure JSON-Formatted Logs in Spring Boot applications with Slf4j, Logback and Logstash
Recipe to configure logging for Spring Data JPA, Hibernate
Recipe to expose Hibernate Statistics through JMX MBeans in Spring Data JPA
Recipe to turn off autocommit for Spring Data JPA, Hibernate, Hikari RESOURCE_LOCAL JPA transactions
How to configure JSON-Formatted Logs in Spring Boot applications with Slf4j, Logback and Logstash
