java - Loading Self Referential Relationships Lazily with Spring Data JPA and Hibernate -


suppose have following entity defined represents , employee , can see there self-referential relationship between manager , employees defined @manytoone , @onetomany relationships respectively. additionally, please note employees attribute has been annotated describing loading behavior lazy.

package demo;  import org.slf4j.logger; import org.slf4j.loggerfactory;  import javax.persistence.*; import java.util.*;  @entity public class employee {     private static final logger logger = loggerfactory.getlogger(employee.class);      @id     @generatedvalue     private long id;      private string name;      @manytoone     private employee manager;      @onetomany(mappedby = "manager", cascade = cascadetype.all, fetch = fetchtype.lazy)     private list<employee> employees;      private employee() {     }      public employee(string name) {         this(name, new employee[]{});     }      public employee(string name, employee... employees) {         this.name = objects.requirenonnull(name);         this.employees = arrays.aslist(employees);     }      public void setmanager(employee manager) {         this.manager = manager;     }      public boolean isdirectmanagerof(employee employee) {         for(employee myemployee: employees) {             if ( myemployee.equals(employee) ) {                 return true;             }         }         return false;     }      @override     public boolean equals(object o) {         if (this == o) return true;         if (o == null || getclass() != o.getclass()) return false;          employee employee = (employee) o;          return name.equals(employee.name);      }      @override     public int hashcode() {         return name.hashcode();     }      @override     public string tostring() {         return name;     } } 

now suppose have repository interface looks following:

package demo;  import org.springframework.data.repository.crudrepository;  public interface employeerepository extends crudrepository<employee, long> {     employee findbyname(string name); } 

i'd understand if 1 employee direct manager of employee. code below defines initial hierarchy , tests behavior.

package demo;  import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication; import org.springframework.context.configurableapplicationcontext;  import javax.persistence.*; import java.util.arrays; import java.util.list; import java.util.objects;  @springbootapplication public class jpacircularreferencetestapplication {     private static final logger logger = loggerfactory.getlogger(jpacircularreferencetestapplication.class);      public static void main(string[] args) {         configurableapplicationcontext context = springapplication.run(jpacircularreferencetestapplication.class, args);          employeerepository employeerepository = context.getbean(employeerepository.class);          employee emp1 = new employee("emp1");         employee emp2 = new employee("emp2");         employee emp3 = new employee("emp3");         employee emp4 = new employee("emp4");          employee director1 = new employee("director1", emp1, emp2);         employee director2 = new employee("director2", emp3, emp4);          employee vp1 = new employee("vp1", director1);         employee vp2 = new employee("vp2", director2);          employee ceo = new employee("ceo", vp1, vp2);          vp1.setmanager(ceo);         vp2.setmanager(ceo);          director1.setmanager(vp1);         director2.setmanager(vp2);          emp1.setmanager(director1);         emp2.setmanager(director1);         emp3.setmanager(director2);         emp4.setmanager(director2);          employeerepository.save(ceo);          employee ceofromrepository = employeerepository.findbyname("ceo");         employee vp1fromrepository = employeerepository.findbyname("vp1");          logger.info("is {} direct manager of {}? {}", ceofromrepository, vp1fromrepository, ceofromrepository.isdirectmanagerof(vp1fromrepository));     } } 

when relationship set lazily load fails exception:

exception in thread "main" org.hibernate.lazyinitializationexception: failed lazily initialize collection of role: demo.employee.employees, not initialize proxy - no session     @ org.hibernate.collection.internal.abstractpersistentcollection.throwlazyinitializationexception(abstractpersistentcollection.java:576)     @ org.hibernate.collection.internal.abstractpersistentcollection.withtemporarysessionifneeded(abstractpersistentcollection.java:215)     @ org.hibernate.collection.internal.abstractpersistentcollection.initialize(abstractpersistentcollection.java:555)     @ org.hibernate.collection.internal.abstractpersistentcollection.read(abstractpersistentcollection.java:143)     @ org.hibernate.collection.internal.persistentbag.iterator(persistentbag.java:294)     @ demo.employee.isdirectmanagerof(employee.java:51)     @ demo.jpacircularreferencetestapplication.main(jpacircularreferencetestapplication.java:52)     @ sun.reflect.nativemethodaccessorimpl.invoke0(native method)     @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)     @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)     @ java.lang.reflect.method.invoke(method.java:497)     @ com.intellij.rt.execution.application.appmain.main(appmain.java:140) 

when relationship set load eagerly there no exception. unfortunately in real use case performance of eagerly loading data unacceptable.

what proper way able run method isdirectmanagerof when want lazily load self-referential relationship?

here pom.xml file reference:

<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" 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"> <modelversion>4.0.0</modelversion>  <groupid>org.test</groupid> <artifactid>demo</artifactid> <version>0.0.1-snapshot</version> <packaging>jar</packaging>  <name>jpa circular reference test</name> <description>demo project spring boot</description>  <parent>     <groupid>org.springframework.boot</groupid>     <artifactid>spring-boot-starter-parent</artifactid>     <version>1.2.6.release</version>     <relativepath/> <!-- lookup parent repository --> </parent>  <properties>     <project.build.sourceencoding>utf-8</project.build.sourceencoding>     <java.version>1.8</java.version> </properties>  <dependencies>     <dependency>         <groupid>org.springframework.boot</groupid>         <artifactid>spring-boot-starter-data-jpa</artifactid>     </dependency>     <dependency>         <groupid>org.hsqldb</groupid>         <artifactid>hsqldb</artifactid>         <scope>runtime</scope>     </dependency>     <dependency>         <groupid>org.springframework.boot</groupid>         <artifactid>spring-boot-starter-test</artifactid>         <scope>test</scope>     </dependency> </dependencies> <build>     <plugins>         <plugin>             <groupid>org.springframework.boot</groupid>             <artifactid>spring-boot-maven-plugin</artifactid>         </plugin>     </plugins> </build> 

as mentioned above m. deinum, following adjustments made , ended resolving problem. first added employeeservice class annotated @transactional follows:

package demo;  import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service; import org.springframework.transaction.annotation.transactional;  @service @transactional public class employeeservice {     @autowired     private employeerepository employeerepository;      public boolean ismanagerof(string managername, string employeename) {         employee manager = employeerepository.findbyname(managername);         employee employee = employeerepository.findbyname(employeename);          return manager.isdirectmanagerof(employee);     } } 

and changed "main" class follows:

package demo;  import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication; import org.springframework.context.configurableapplicationcontext;  @springbootapplication public class jpacircularreferencetestapplication {     private static final logger logger = loggerfactory.getlogger(jpacircularreferencetestapplication.class);      public static void main(string[] args) {         configurableapplicationcontext context = springapplication.run(jpacircularreferencetestapplication.class, args);          employeerepository employeerepository = context.getbean(employeerepository.class);          employee emp1 = new employee("emp1");         employee emp2 = new employee("emp2");         employee emp3 = new employee("emp3");         employee emp4 = new employee("emp4");          employee director1 = new employee("director1", emp1, emp2);         employee director2 = new employee("director2", emp3, emp4);          employee vp1 = new employee("vp1", director1);         employee vp2 = new employee("vp2", director2);          employee ceo = new employee("ceo", vp1, vp2);          vp1.setmanager(ceo);         vp2.setmanager(ceo);          director1.setmanager(vp1);         director2.setmanager(vp2);          emp1.setmanager(director1);         emp2.setmanager(director1);         emp3.setmanager(director2);         emp4.setmanager(director2);          employeerepository.save(ceo);          employeeservice employeeservice = context.getbean(employeeservice.class);          logger.info("is {} direct manager of {}? {}", "ceo", "vp1", employeeservice.ismanagerof("ceo", "vp1"));     } } 

Comments