diff --git a/persistence-modules/spring-boot-persistence-h2/pom.xml b/persistence-modules/spring-boot-persistence-h2/pom.xml
index 5b5e255211..777bc6cb2d 100644
--- a/persistence-modules/spring-boot-persistence-h2/pom.xml
+++ b/persistence-modules/spring-boot-persistence-h2/pom.xml
@@ -29,12 +29,30 @@
com.h2database
h2
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ compile
+
+
+ org.hibernate
+ hibernate-core
+ ${hibernate.version}
+
+
+ com.vladmihalcea
+ db-util
+ ${db-util.version}
+
com.baeldung.h2db.demo.server.SpringBootApp
2.0.4.RELEASE
+ 5.3.11.Final
+ 1.0.4
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/LazyLoadNoTransSpringBootApplication.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/LazyLoadNoTransSpringBootApplication.java
new file mode 100644
index 0000000000..a52d9fc2f9
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/LazyLoadNoTransSpringBootApplication.java
@@ -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);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/config/DatasourceProxyBeanPostProcessor.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/config/DatasourceProxyBeanPostProcessor.java
new file mode 100644
index 0000000000..c087427b65
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/config/DatasourceProxyBeanPostProcessor.java
@@ -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();
+ }
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/Document.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/Document.java
new file mode 100644
index 0000000000..9d69e7eb58
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/Document.java
@@ -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;
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/User.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/User.java
new file mode 100644
index 0000000000..ae9cb9e4e8
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/entity/User.java
@@ -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 docs = new ArrayList<>();
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/repository/UserRepository.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/repository/UserRepository.java
new file mode 100644
index 0000000000..bafe484163
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/repository/UserRepository.java
@@ -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 {
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/service/ServiceLayer.java b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/service/ServiceLayer.java
new file mode 100644
index 0000000000..ff3783fd9d
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/java/com/baeldung/h2db/lazy_load_no_trans/service/ServiceLayer.java
@@ -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();
+ }
+
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-off.properties b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-off.properties
new file mode 100644
index 0000000000..b5fb841685
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-off.properties
@@ -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
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-on.properties b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-on.properties
new file mode 100644
index 0000000000..04579e1dae
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application-lazy-load-no-trans-on.properties
@@ -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
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/data.sql b/persistence-modules/spring-boot-persistence-h2/src/main/resources/data.sql
index 2d7b446005..b8835e70cb 100644
--- a/persistence-modules/spring-boot-persistence-h2/src/main/resources/data.sql
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/data.sql
@@ -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');
\ No newline at end of file
+('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);
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOffIntegrationTest.java b/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOffIntegrationTest.java
new file mode 100644
index 0000000000..cb5063d8a4
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOffIntegrationTest.java
@@ -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);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOnIntegrationTest.java b/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOnIntegrationTest.java
new file mode 100644
index 0000000000..5968fde7b7
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-h2/src/test/java/com/baeldung/lazy_load_no_trans/LazyLoadNoTransPropertyOnIntegrationTest.java
@@ -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);
+ }
+}