Merge pull request #6774 from isaolmez/BAEL-2809
BAEL-2809 Hibernate Batch Inserts
This commit is contained in:
commit
b5d95430f8
|
@ -26,6 +26,12 @@
|
|||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.ttddyy</groupId>
|
||||
<artifactId>datasource-proxy</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
@ -35,7 +41,5 @@
|
|||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-oxm</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Student> 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<Student> getStudents() {
|
||||
return students;
|
||||
}
|
||||
|
||||
public void setStudents(List<Student> students) {
|
||||
this.students = students;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
spring.jpa.show-sql=false
|
||||
|
||||
spring.jpa.properties.hibernate.jdbc.batch_size=5
|
||||
spring.jpa.properties.hibernate.order_inserts=true
|
||||
spring.jpa.properties.hibernate.order_updates=true
|
||||
spring.jpa.properties.hibernate.batch_versioned_data=true
|
|
@ -0,0 +1,94 @@
|
|||
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.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.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;
|
||||
|
||||
@Transactional
|
||||
@Test
|
||||
public void whenInsertingSingleTypeOfEntity_thenCreatesSingleBatch() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
School school = createSchool(i);
|
||||
entityManager.persist(school);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Test
|
||||
public void whenUpdatingEntities_thenCreatesBatch() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
School school = createSchool(i);
|
||||
entityManager.persist(school);
|
||||
}
|
||||
|
||||
entityManager.flush();
|
||||
|
||||
TypedQuery<School> schoolQuery = entityManager.createQuery("SELECT s from School s", School.class);
|
||||
List<School> allSchools = schoolQuery.getResultList();
|
||||
|
||||
for (School school : allSchools) {
|
||||
school.setName("Updated_" + school.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
entityManager.flush();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue