使用Spring Data JPA和Hibernate加载自引用关系

时间:2015-09-25 19:02:33

标签: java spring hibernate jpa spring-data

假设我定义了以下Entity代表Employee,您可以看到manageremployees之间存在自我引用关系{分别为{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

1 个答案:

答案 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"));
    }
}