2015-09-25 130 views
0

假设我有以下Entity定义的代表和Employee,正如你可以看到有分别与@ManyToOne@OneToMany关系定义manageremployees之间的自我指涉的关系。此外,请注意,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> 

+3

您没有会话/实体管理器了,因此你的懒惰集合将失败(这是集合的默认设置,因此您不需要添加任何LAZY)。把你的代码放在一个用@ Transactional注释的服务中,在交易期间实体管理器保持打开状态。 –

+0

谢谢@ M.Deinum那样做! – Centinul

回答

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); 
    } 
} 

再变“主”类,如下所示:

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")); 
    } 
} 
相关问题