From 6ce082627634aa5ddb82044f5d7f170c15e00f4a Mon Sep 17 00:00:00 2001 From: Vinc Conrad Date: Mon, 2 Dec 2019 17:45:03 +0100 Subject: [PATCH 1/2] add additional test cases --- .../LazyInitializationExceptionTest.java | 179 +++++++++++++++++- 1 file changed, 171 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java b/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java index 41b621d..08a7fc9 100644 --- a/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java +++ b/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java @@ -1,15 +1,22 @@ package com.example.hibernatelazyinitializationexception; import com.example.hibernatelazyinitializationexception.entity.Employee; +import com.example.hibernatelazyinitializationexception.entity.Issue; import com.example.hibernatelazyinitializationexception.repository.EmployeeRepository; +import org.hibernate.Hibernate; 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.junit4.SpringRunner; +import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; import java.util.Collection; import java.util.Optional; @@ -25,26 +32,182 @@ public class LazyInitializationExceptionTest { @Autowired EmployeeRepository repository; + @Autowired + EntityManagerFactory entityManagerFactory; + @Autowired + EntityManager entityManager; + @Autowired + TransactionTemplate transactionTemplate; @Test public void noSession_onlyGetter() { repository.findById(1).map(Employee::getIssues); } - + + @Test(expected = LazyInitializationException.class) public void noSession_consumingCollection() { - repository.findById(2).map(Employee::getIssues).map(Collection::size); + Collection issues = repository.findById(2).get().getIssues(); + issues.size(); + } + + + @Test + public void manualSession_hibernateInitialize_consumingCollectionOutsideOfSession() { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + //the entity manager i am using here, is not the one spring is using! So i must not use repository.findById(); + Employee employee = entityManager.find(Employee.class,2); + Hibernate.initialize(employee.getIssues()); + + entityManager.getTransaction().commit(); + entityManager.close(); + + //Session closed now + //I should be able to use getIssues now without Session/Transaction bc I used Hibernate.initialize(employee.getIssues()); + employee.getIssues().size(); + } + + @Test(expected = LazyInitializationException.class) + public void manualSession_hibernateInitializeWholeEntity_consumingCollectionOutsideOfSession() { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + //the entity manager i am using here, is not the one spring is using! So i must not use repository.findById(); + Employee employee = entityManager.find(Employee.class,2); + //initializing whole Entity, not explicitly collection + //wont work cause Hibernate.initialize() does not work recursively + Hibernate.initialize(employee); + + entityManager.getTransaction().commit(); + entityManager.close(); + + //Session closed now + //I should be able to use getIssues now without Session/Transaction bc I used Hibernate.initialize(employee.getIssues()); + employee.getIssues().size(); + } + + + @Test(expected = LazyInitializationException.class) + public void manualSession_consumingCollectionOutsideOfSession() { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + //the entity manager i am using here, is not the one spring is using! So i must not use repository.findById(); + Employee employee = entityManager.find(Employee.class,2); + + entityManager.getTransaction().commit(); + entityManager.close(); + //Session closed now + //I should NOT be able to use getIssues now without Session/Transaction bc I did NOT used Hibernate.initialize(employee.getIssues()); + employee.getIssues().size(); + } + + @Test() + public void manualSession_consumingCollection() { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + Employee employee = entityManager.find(Employee.class,2); + Collection issues = employee.getIssues(); + issues.size(); + + entityManager.getTransaction().commit(); + entityManager.close(); + } + + @Test(expected = IllegalStateException.class) + public void manualTransactional_useInjectedEntityManager(){ + entityManager.getTransaction().begin(); + + //Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead + //Spring tells us to use its api for manual transaction handling and not just inject the entity manager and issue queries ourselfes + Employee employee = repository.findById(2).get(); + Collection issues = employee.getIssues(); + issues.size(); + + entityManager.getTransaction().commit(); + entityManager.close(); } @Test @Transactional public void transactional() { -// given - Optional collectionSizeOptional = repository.findById(3).map(Employee::getIssues).map(Collection::size); - -// expect - assertThat(collectionSizeOptional.isPresent(), is(true)); - assertThat(collectionSizeOptional.get(), is(1)); + Employee employee = repository.findById(2).get(); + employee.getIssues().size(); + } + + + @Test + public void transactionTemplate_hibernateInitialize_consumingCollectionOutsideOfSession() { + Employee employee = transactionTemplate.execute(new TransactionCallback() { + + @Override + public Employee doInTransaction(TransactionStatus status) { + Employee employeeOptional = repository.findById(2).get(); + Hibernate.initialize(employeeOptional.getIssues()); + return employeeOptional; + } + }); + + //Session is closed now, trying to access lazy fetched collection + employee.getIssues().size(); } + + @Test(expected = LazyInitializationException.class) + public void transactionTemplate_consumingCollectionOutsideOfSession() { + Employee employee = transactionTemplate.execute(new TransactionCallback() { + + @Override + public Employee doInTransaction(TransactionStatus status) { + Employee employeeOptional = repository.findById(2).get(); + return employeeOptional; + } + }); + + //Session is closed now, trying to access lazy fetched collection + employee.getIssues().size(); + } + + @Test(expected = LazyInitializationException.class) + public void externalTransactionalAnnotatedMethod_hibernateInitialize_consumingCollectionOutsideOfSession() { + //Does not work, idk why + Employee employee = loadEmployeeAndInitializeCollection(2); + + //Session is closed now, trying to access lazy fetched collection + employee.getIssues().size(); + } + + + @Transactional + public Employee loadEmployeeAndInitializeCollection(Integer id){ + Employee employee = repository.findById(id).get(); + Hibernate.initialize(employee.getIssues()); + return employee; + } + + + @Test + public void externalTransactionTemplateMethod_hibernateInitialize_consumingCollectionOutsideOfSession() { + Employee employee = loadEmployeeAndInitializeCollection_withTransactionalTemplate(2); + + //Session is closed now, trying to access lazy fetched collection + employee.getIssues().size(); + } + + public Employee loadEmployeeAndInitializeCollection_withTransactionalTemplate(Integer id){ + + return transactionTemplate.execute(new TransactionCallback() { + + @Override + public Employee doInTransaction(TransactionStatus status) { + Employee employee = repository.findById(id).get(); + Hibernate.initialize(employee.getIssues()); + return employee; + } + }); + } + } From 344444a6e41d6befb01732e6d85c2474f2b82d0a Mon Sep 17 00:00:00 2001 From: Vinc Conrad Date: Tue, 3 Dec 2019 09:44:54 +0100 Subject: [PATCH 2/2] add Transactionmanager Test --- .../LazyInitializationExceptionTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java b/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java index 08a7fc9..7fe8bc8 100644 --- a/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java +++ b/src/test/java/com/example/hibernatelazyinitializationexception/LazyInitializationExceptionTest.java @@ -10,13 +10,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.transaction.TransactionManager; import java.util.Collection; import java.util.Optional; @@ -38,6 +41,8 @@ public class LazyInitializationExceptionTest { EntityManager entityManager; @Autowired TransactionTemplate transactionTemplate; + @Autowired + PlatformTransactionManager transactionManager; @Test public void noSession_onlyGetter() { @@ -209,5 +214,26 @@ public Employee doInTransaction(TransactionStatus status) { }); } + + @Test + public void platformTransactionManager_initializeHibernate_consumingCollectionOutsideOfSession(){ + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + Employee employee =null; + TransactionStatus status = transactionManager.getTransaction(def); + try { + // execute your business logic here + employee = repository.findById(2).get(); + Hibernate.initialize(employee.getIssues()); + } + catch (Exception ex) { + transactionManager.rollback(status); + throw ex; + } + transactionManager.commit(status); + + //Session is closed now, trying to access lazy fetched collection + employee.getIssues().size(); + } + }