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
Post a Comment