lazy_load_no_trans init commit (#8736)
* lazy_load_no_trans init commit * refactoring * methods naming * Update persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOnIntegrationTest.java Co-Authored-By: KevinGilmore <kpg102@gmail.com> * Update persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOnIntegrationTest.java Co-Authored-By: KevinGilmore <kpg102@gmail.com> * Update persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOffIntegrationTest.java Co-Authored-By: KevinGilmore <kpg102@gmail.com> * naming * code readability Co-authored-by: admin <git-commit-id-plugin> Co-authored-by: KevinGilmore <kpg102@gmail.com>
This commit is contained in:
parent
1528acf120
commit
a94b56c74e
|
@ -29,12 +29,30 @@
|
|||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladmihalcea</groupId>
|
||||
<artifactId>db-util</artifactId>
|
||||
<version>${db-util.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<!-- The main class to start by executing java -jar -->
|
||||
<start-class>com.baeldung.h2db.demo.server.SpringBootApp</start-class>
|
||||
<spring-boot.version>2.0.4.RELEASE</spring-boot.version>
|
||||
<hibernate.version>5.3.11.Final</hibernate.version>
|
||||
<db-util.version>1.0.4</db-util.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableTransactionManagement
|
||||
public class LazyLoadNoTransSpringBootApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LazyLoadNoTransSpringBootApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans.config;
|
||||
|
||||
import net.ttddyy.dsproxy.listener.DataSourceQueryCountListener;
|
||||
import net.ttddyy.dsproxy.listener.logging.CommonsQueryLoggingListener;
|
||||
import net.ttddyy.dsproxy.listener.logging.DefaultQueryLogEntryCreator;
|
||||
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
||||
import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener;
|
||||
import net.ttddyy.dsproxy.support.ProxyDataSource;
|
||||
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.factory.config.BeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Component
|
||||
public class DatasourceProxyBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
if (bean instanceof DataSource && !(bean instanceof ProxyDataSource)) {
|
||||
// Instead of directly returning a less specific datasource bean
|
||||
// (e.g.: HikariDataSource -> DataSource), return a proxy object.
|
||||
// See following links for why:
|
||||
// https://stackoverflow.com/questions/44237787/how-to-use-user-defined-database-proxy-in-datajpatest
|
||||
// https://gitter.im/spring-projects/spring-boot?at=5983602d2723db8d5e70a904
|
||||
// http://blog.arnoldgalovics.com/2017/06/26/configuring-a-datasource-proxy-in-spring-boot/
|
||||
final ProxyFactory factory = new ProxyFactory(bean);
|
||||
factory.setProxyTargetClass(true);
|
||||
factory.addAdvice(new ProxyDataSourceInterceptor((DataSource) bean));
|
||||
return factory.getProxy();
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private static class ProxyDataSourceInterceptor implements MethodInterceptor {
|
||||
private final DataSource dataSource;
|
||||
|
||||
public ProxyDataSourceInterceptor(final DataSource dataSource) {
|
||||
this.dataSource = ProxyDataSourceBuilder.create(dataSource)
|
||||
.name("MyDS")
|
||||
.multiline()
|
||||
.logQueryBySlf4j(SLF4JLogLevel.INFO)
|
||||
.listener(new DataSourceQueryCountListener())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
final Method proxyMethod = ReflectionUtils.findMethod(this.dataSource.getClass(),
|
||||
invocation.getMethod().getName());
|
||||
if (proxyMethod != null) {
|
||||
return proxyMethod.invoke(this.dataSource, invocation.getArguments());
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Immutable
|
||||
public class Document {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
|
||||
private Long userId;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Immutable
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String comment;
|
||||
|
||||
@OneToMany(mappedBy = "userId")
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
private List<Document> docs = new ArrayList<>();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans.repository;
|
||||
|
||||
import com.baeldung.h2db.lazy_load_no_trans.entity.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.baeldung.h2db.lazy_load_no_trans.service;
|
||||
|
||||
import com.baeldung.h2db.lazy_load_no_trans.entity.User;
|
||||
import com.baeldung.h2db.lazy_load_no_trans.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Service
|
||||
public class ServiceLayer {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public long countAllDocsTransactional() {
|
||||
return countAllDocs();
|
||||
}
|
||||
|
||||
public long countAllDocsNonTransactional() {
|
||||
return countAllDocs();
|
||||
}
|
||||
|
||||
private long countAllDocs() {
|
||||
return userRepository.findAll()
|
||||
.stream()
|
||||
.map(User::getDocs)
|
||||
.mapToLong(Collection::size)
|
||||
.sum();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
spring.datasource.url=jdbc:h2:mem:mydb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.path=/h2-console
|
||||
|
||||
logging.level.org.hibernate.SQL=INFO
|
||||
logging.level.org.hibernate.type=TRACE
|
||||
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
|
||||
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false
|
||||
spring.jpa.open-in-view=false
|
|
@ -0,0 +1,13 @@
|
|||
spring.datasource.url=jdbc:h2:mem:mydb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.path=/h2-console
|
||||
|
||||
logging.level.org.hibernate.SQL=INFO
|
||||
logging.level.org.hibernate.type=TRACE
|
||||
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
|
||||
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
|
||||
spring.jpa.open-in-view=false
|
|
@ -10,4 +10,17 @@ CREATE TABLE billionaires (
|
|||
INSERT INTO billionaires (first_name, last_name, career) VALUES
|
||||
('Aliko', 'Dangote', 'Billionaire Industrialist'),
|
||||
('Bill', 'Gates', 'Billionaire Tech Entrepreneur'),
|
||||
('Folrunsho', 'Alakija', 'Billionaire Oil Magnate');
|
||||
('Folrunsho', 'Alakija', 'Billionaire Oil Magnate');
|
||||
|
||||
insert into USER values (101, 'user1', 'comment1');
|
||||
insert into USER values (102, 'user2', 'comment2');
|
||||
insert into USER values (103, 'user3', 'comment3');
|
||||
insert into USER values (104, 'user4', 'comment4');
|
||||
insert into USER values (105, 'user5', 'comment5');
|
||||
|
||||
insert into DOCUMENT values (1, 'doc1', 101);
|
||||
insert into DOCUMENT values (2, 'doc2', 101);
|
||||
insert into DOCUMENT values (3, 'doc3', 101);
|
||||
insert into DOCUMENT values (4, 'doc4', 101);
|
||||
insert into DOCUMENT values (5, 'doc5', 102);
|
||||
insert into DOCUMENT values (6, 'doc6', 102);
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.lazy_load_no_trans;
|
||||
|
||||
import com.baeldung.h2db.lazy_load_no_trans.LazyLoadNoTransSpringBootApplication;
|
||||
import com.baeldung.h2db.lazy_load_no_trans.service.ServiceLayer;
|
||||
import com.vladmihalcea.sql.SQLStatementCountValidator;
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = LazyLoadNoTransSpringBootApplication.class)
|
||||
@ActiveProfiles("lazy-load-no-trans-off")
|
||||
public class LazyLoadNoTransPropertyOffIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ServiceLayer serviceLayer;
|
||||
|
||||
private static final long EXPECTED_DOCS_COLLECTION_SIZE = 6;
|
||||
|
||||
@Test(expected = LazyInitializationException.class)
|
||||
public void whenCallNonTransactionalMethodWithPropertyOff_thenThrowException() {
|
||||
serviceLayer.countAllDocsNonTransactional();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCallTransactionalMethodWithPropertyOff_thenTestPass() {
|
||||
SQLStatementCountValidator.reset();
|
||||
|
||||
long docsCount = serviceLayer.countAllDocsTransactional();
|
||||
|
||||
assertEquals(EXPECTED_DOCS_COLLECTION_SIZE, docsCount);
|
||||
|
||||
SQLStatementCountValidator.assertSelectCount(2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.baeldung.lazy_load_no_trans;
|
||||
|
||||
import com.baeldung.h2db.lazy_load_no_trans.LazyLoadNoTransSpringBootApplication;
|
||||
import com.baeldung.h2db.lazy_load_no_trans.service.ServiceLayer;
|
||||
import com.vladmihalcea.sql.SQLStatementCountValidator;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = LazyLoadNoTransSpringBootApplication.class)
|
||||
@ActiveProfiles("lazy-load-no-trans-on")
|
||||
public class LazyLoadNoTransPropertyOnIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ServiceLayer serviceLayer;
|
||||
|
||||
private static final long EXPECTED_DOCS_COLLECTION_SIZE = 6;
|
||||
private static final long EXPECTED_USERS_COUNT = 5;
|
||||
|
||||
@Test
|
||||
public void whenCallNonTransactionalMethodWithPropertyOn_thenGetNplusOne() {
|
||||
SQLStatementCountValidator.reset();
|
||||
|
||||
long docsCount = serviceLayer.countAllDocsNonTransactional();
|
||||
|
||||
assertEquals(EXPECTED_DOCS_COLLECTION_SIZE, docsCount);
|
||||
|
||||
SQLStatementCountValidator.assertSelectCount(EXPECTED_USERS_COUNT + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCallTransactionalMethodWithPropertyOn_thenTestPass() {
|
||||
SQLStatementCountValidator.reset();
|
||||
|
||||
long docsCount = serviceLayer.countAllDocsTransactional();
|
||||
|
||||
assertEquals(EXPECTED_DOCS_COLLECTION_SIZE, docsCount);
|
||||
|
||||
SQLStatementCountValidator.assertSelectCount(2);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue