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