14 Sep 2017

Spring JPA Hibernate - JpaRepository Insert (Batch)

How to use batch in spring data JpaRepository

Configuration for batch size

      hibernate.jdbc.batch_size: 4

Rewrite INSERT statements into multi-value inserts (only for MySQL)

  1. append &rewriteBatchedStatements=true to spring.datasource.url

Verification by db log (MySQL as example)

If turn on show-sql for enable logging of SQL statements, log will still include multiple insert into sql. It is because sql rewriting is done at PreparedStatement.executeBatchInternal after sql statement print at AbstractBatchImpl.getBatchStatement, and we could verify the real sql by db log:

$ mysqld --verbose --help | grep -A 1 "Default options"
mysql> SET GLOBAL log_output = "FILE";
mysql> SET GLOBAL general_log_file = "/path/to/your/logfile.log";
mysql> SET GLOBAL general_log = 'ON';
$ tail -f /path/to/your/logfile.log

SQL will be like:

178931 16:16:16	  66 Query	SELECT 1
	  566 Query	SET autocommit=0
	  566 Query	select @@session.tx_read_only
	  566 Query	insert into  emp (id, name, dep) values ('1', 'clark', 'IT'),('2', 'robin', 'Market'),('3', 'jeff', 'IT'),('4', 'emily', 'HR')
	  566 Query	insert into  emp (id, name, dep) values ('5', 'john', 'Sales')

How does spring data internally work ?

Add Insert Action

  1. call SimpleJpaRepository.save (org.springframework.data.jpa.repository.support)
  2. AbstractEntityManagerImpl.persist (org.hibernate.jpa.spi)
  3. SessionImpl.persist (org.hibernate.internal)
  4. SessionImpl.firePersist (org.hibernate.internal)
  5. DefaultPersistEventListener.onPersist (org.hibernate.event.internal)
  6. DefaultPersistEventListener.entityIsTransient (org.hibernate.event.internal)
  7. JpaPersistEventListener.saveWithGeneratedId (org.hibernate.jpa.event.internal.core)
  8. AbstractSaveEventListener.performSave (org.hibernate.event.internal)
  9. AbstractSaveEventListener.performSaveOrReplicate (org.hibernate.event.internal)
  10. AbstractSaveEventListener.addInsertAction (org.hibernate.event.internal)
  11. ActionQueue.addAction (org.hibernate.engine.spi)
  12. ActionQueue.addInsertAction (org.hibernate.engine.spi)
  13. ActionQueue.addResolvedEntityInsertAction (org.hibernate.engine.spi)
  14. add EntityInsertAction into EXECUTABLE_LISTS_MAP.get(AbstractEntityInsertAction)

Real insert execution when session flush

  1. TransactionInterceptor.invoke (org.springframework.transaction.interceptor): it is called by proxy of business class whose method with @Transactional
  2. TransactionAspectSupport.commitTransactionAfterReturning (org.springframework.transaction.interceptor): it is called after real business method
  3. SessionImpl.beforeTransactionCompletion (org.hibernate.internal)
  4. SessionImpl.flush (org.hibernate.internal): get all flush type listeners’ (JpaFlushEventListener set from JpaIntegrator) onFlush
  5. DefaultFlushEventListener.onFlush (org.hibernate.event.internal): call super’s performExecutions
  6. ActionQueue.executeActions (org.hibernate.engine.spi): loop EXECUTABLE_LISTS_MAP.get(AbstractEntityInsertAction to execute every action.
  7. EntityInsertAction.execute (org.hibernate.action.internal): call SingleTableEntityPersister.insert
  8. AbstractEntityPersister.insert (org.hibernate.persister.entity): if use batch, add insert action to BatchingBatch
  9. BatchingBatch.addToBatch (org.hibernate.engine.jdbc.batch.internal): if reached the batch_size, then perform execution
  10. BatchingBatch.performExecution (org.hibernate.engine.jdbc.batch.internal)
  11. PreparedStatement.executeBatchInternal (com.mysql.jdbc): should set rewriteBatchedStatements if want to execute within batch sql
  12. PreparedStatement.executeBatchedInserts (com.mysql.jdbc): Rewrites the already prepared statement into a multi-value insert and executes enw statement