diff --git a/persistence-modules/spring-data-jpa-2/pom.xml b/persistence-modules/spring-data-jpa-2/pom.xml
index 8e46112659..8d79647544 100644
--- a/persistence-modules/spring-data-jpa-2/pom.xml
+++ b/persistence-modules/spring-data-jpa-2/pom.xml
@@ -1,12 +1,12 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- com.baeldung
+ com.baeldung
spring-data-jpa-2
- spring-data-jpa
-
+ spring-data-jpa
+
parent-boot-2
com.baeldung
@@ -19,12 +19,17 @@
org.springframework.boot
spring-boot-starter-data-jpa
-
+
com.h2database
h2
-
+
+
+ net.ttddyy
+ datasource-proxy
+ 1.4.1
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java
new file mode 100644
index 0000000000..504357db44
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java
@@ -0,0 +1,53 @@
+package com.baeldung.batchinserts;
+
+import java.lang.reflect.Method;
+import javax.sql.DataSource;
+import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+@Component
+@Profile("batchinserts")
+public class DatasourceProxyBeanPostProcessor implements BeanPostProcessor {
+
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ if (bean instanceof DataSource) {
+ ProxyFactory factory = new ProxyFactory(bean);
+ factory.setProxyTargetClass(true);
+ factory.addAdvice(new ProxyDataSourceInterceptor((DataSource) bean));
+ return factory.getProxy();
+ }
+
+ return bean;
+ }
+
+ private static class ProxyDataSourceInterceptor implements MethodInterceptor {
+
+ private final DataSource dataSource;
+
+ public ProxyDataSourceInterceptor(final DataSource dataSource) {
+ this.dataSource = ProxyDataSourceBuilder.create(dataSource).name("Batch-Insert-Logger").asJson().countQuery().logQueryToSysOut().build();
+ }
+
+ @Override
+ public Object invoke(final MethodInvocation invocation) throws Throwable {
+ Method proxyMethod = ReflectionUtils.findMethod(dataSource.getClass(), invocation.getMethod().getName());
+ if (proxyMethod != null) {
+ return proxyMethod.invoke(dataSource, invocation.getArguments());
+ }
+ return invocation.proceed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/School.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/School.java
new file mode 100644
index 0000000000..6d2f333ac7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/School.java
@@ -0,0 +1,45 @@
+package com.baeldung.batchinserts.model;
+
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class School {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+
+ private String name;
+
+ @OneToMany(mappedBy = "school")
+ private List students;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getStudents() {
+ return students;
+ }
+
+ public void setStudents(List students) {
+ this.students = students;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/Student.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/Student.java
new file mode 100644
index 0000000000..d38214f122
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/batchinserts/model/Student.java
@@ -0,0 +1,44 @@
+package com.baeldung.batchinserts.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Student {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+
+ private String name;
+
+ @ManyToOne
+ private School school;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public School getSchool() {
+ return school;
+ }
+
+ public void setSchool(School school) {
+ this.school = school;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/resources/application-batchinserts.properties b/persistence-modules/spring-data-jpa-2/src/main/resources/application-batchinserts.properties
new file mode 100644
index 0000000000..bd37e8c155
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/resources/application-batchinserts.properties
@@ -0,0 +1,4 @@
+spring.jpa.show-sql=false
+
+spring.jpa.properties.hibernate.jdbc.batch_size=5
+spring.jpa.properties.hibernate.order_inserts=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java
new file mode 100644
index 0000000000..dd61b7b6df
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java
@@ -0,0 +1,92 @@
+package com.baeldung.batchinserts;
+
+import static com.baeldung.batchinserts.TestObjectHelper.createSchool;
+import static com.baeldung.batchinserts.TestObjectHelper.createStudent;
+
+import com.baeldung.batchinserts.model.School;
+import com.baeldung.batchinserts.model.Student;
+import java.util.List;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@Transactional
+@ActiveProfiles("batchinserts")
+public class JpaBatchInsertsIntegrationTest {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ private static final int BATCH_SIZE = 5;
+
+ @Test
+ public void whenInsertingSingleTypeOfEntity_thenCreatesSingleBatch() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+
+ entityManager.flush();
+ }
+
+ @Test
+ public void whenFlushingAfterBatch_ThenClearsMemory() {
+ for (int i = 0; i < 10; i++) {
+ if (i > 0 && i % BATCH_SIZE == 0) {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+
+ entityManager.flush();
+ }
+
+ @Test
+ public void whenThereAreMultipleEntities_ThenCreatesNewBatch() {
+ for (int i = 0; i < 10; i++) {
+ if (i > 0 && i % BATCH_SIZE == 0) {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ School school = createSchool(i);
+ entityManager.persist(school);
+ Student firstStudent = createStudent(school);
+ Student secondStudent = createStudent(school);
+ entityManager.persist(firstStudent);
+ entityManager.persist(secondStudent);
+ }
+
+ entityManager.flush();
+ }
+
+ @Test
+ public void whenUpdatingEntities_thenCreatesBatch() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+
+ entityManager.flush();
+
+ TypedQuery schoolQuery = entityManager.createQuery("SELECT s from School s", School.class);
+ List allSchools = schoolQuery.getResultList();
+
+ for (School school : allSchools) {
+ school.setName("Updated_" + school.getName());
+ }
+
+ entityManager.flush();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java
new file mode 100644
index 0000000000..20502c793d
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.batchinserts;
+
+import static com.baeldung.batchinserts.TestObjectHelper.createSchool;
+
+import com.baeldung.batchinserts.model.School;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@Transactional
+@ActiveProfiles("batchinserts")
+@TestPropertySource(properties = "spring.jpa.properties.hibernate.jdbc.batch_size=-1")
+public class JpaNoBatchInsertsIntegrationTest {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Test
+ public void whenNotConfigured_ThenSendsInsertsSeparately() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+ }
+
+ @After
+ public void tearDown() {
+ entityManager.flush();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java
new file mode 100644
index 0000000000..fcd26cb721
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java
@@ -0,0 +1,20 @@
+package com.baeldung.batchinserts;
+
+import com.baeldung.batchinserts.model.School;
+import com.baeldung.batchinserts.model.Student;
+
+public class TestObjectHelper {
+
+ public static School createSchool(int nameIdentifier) {
+ School school = new School();
+ school.setName("School" + (nameIdentifier + 1));
+ return school;
+ }
+
+ public static Student createStudent(School school) {
+ Student student = new Student();
+ student.setName("Student-" + school.getName());
+ student.setSchool(school);
+ return student;
+ }
+}