假设我定义了以下Entity
代表Employee
,您可以看到manager
和employees
之间存在自我引用关系{分别为{1}}和@ManyToOne
个关系。另请注意,@OneToMany
属性已注释,将加载行为描述为employees
。
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;
}
}
界面,如下所示:
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
答案 0 :(得分:0)
正如上面提到的M. Deinum,进行了以下调整并最终解决了问题。首先,我添加了一个用EmployeeService
注释的@Transactional
类,如下所示:
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);
}
}
然后按如下方式更改“main”类:
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"));
}
}