0
假設我有以下Entity
定義的代表和Employee
,正如你可以看到有分別與@ManyToOne
和@OneToMany
關係定義manager
和employees
之間的自我指涉的關係。此外,請注意,employees
屬性已註釋,描述加載行爲爲LAZY
。加載自參照關係懶洋洋地使用Spring數據JPA和Hibernate
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;
}
}
現在假設我有一個Repository
界面,如下所示:
package demo;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
Employee findByName(String name);
}
我想知道,如果一個Employee
是另一個Employee
的直接管理者。下面的代碼定義了一個初始層次並測試行爲。
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));
}
}
當關系設置爲懶惰地加載該失敗,異常:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: demo.Employee.employees, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:215)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:555)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:143)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294)
at demo.Employee.isDirectManagerOf(Employee.java:51)
at demo.JpaCircularReferenceTestApplication.main(JpaCircularReferenceTestApplication.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
當關系設置爲加載熱切沒有異常。不幸的是,在真實的用例中,急切加載數據的性能是不可接受的。
想要延遲加載自引用關係時,能夠運行isDirectManagerOf
這樣的方法的正確方法是什麼?
這裏是pom.xml
文件供參考:
<?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 for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from 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>
您沒有會話/實體管理器了,因此你的懶惰集合將失敗(這是集合的默認設置,因此您不需要添加任何LAZY)。把你的代碼放在一個用@ Transactional註釋的服務中,在交易期間實體管理器保持打開狀態。 –
謝謝@ M.Deinum那樣做! – Centinul