JPA 中的双向关系

Bidirectional relationships in JPA

我遗漏了一些非常基本的东西。下面给出两个实体 Department(反面)和 Employee(拥有方)形成从 DepartmentEmployee.

的一对多关系

Department.java

@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
    @UniqueConstraint(columnNames = {"department_id"})})
public class Department implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "department_id", nullable = false)
    private Long departmentId;

    @Column(name = "department_name", length = 255)
    private String departmentName;

    @Column(length = 255)
    private String location;

    @OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
    private List<Employee> employeeList = new ArrayList<Employee>(0);

    private static final long serialVersionUID = 1L;

    // Constructors + getters + setters + hashcode() + equals() + toString().
    // No defensive link (relationship) management methods have yet been added to.
    // CascadeType is also kept at a distance for now.
}

Employee.java

@Entity
@Table(catalog = "testdb", schema = "", uniqueConstraints = {
    @UniqueConstraint(columnNames = {"employee_id"})})
public class Employee implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "employee_id", nullable = false)
    private Long employeeId;

    @Column(name = "employee_name", length = 255)
    private String employeeName;

    @JoinColumn(name = "department_id", referencedColumnName = "department_id")
    @ManyToOne(fetch = FetchType.LAZY)
    private Department department;

    private static final long serialVersionUID = 1L;

    // Constructors + getters + setters + hashcode() + equals() + toString().
    // No defensive link (relationship) management methods have yet been added to.
    // CascadeType is also kept at a distance for now.
}

下面给出了一些在无状态 EJB(使用 CMT)中分别执行持久化、合并和删除操作的方法。

public List<Employee> persist() {
    Employee employee = new Employee();
    employee.setEmployeeName("a");
    employee.setDepartment(entityManager.getReference(Department.class, 1L));
    entityManager.persist(employee);
    return employee.getDepartment().getEmployeeList();
}

public List<Employee> merge(Employee employee) {
    employee.setEmployeeName("b");
    employee.setDepartment(entityManager.getReference(Department.class, 1L));
    return entityManager.merge(employee).getDepartment().getEmployeeList();
}

public List<Employee> remove(Employee employee) {
    entityManager.remove(entityManager.contains(employee) ? employee : entityManager.merge(employee));
    return entityManager.getReference(Employee.class, employee.getEmployeeId()).getDepartment().getEmployeeList();
}

public Employee getEmployeeById(Long id) {
    return entityManager.find(Employee.class, id);
}

这些方法由关联的应用程序客户端在非事务环境中依次(一个接一个)调用。

List<Employee> persistedList = employeeSessionBean.persist();
for (Employee employee : persistedList) {
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}

List<Employee> mergedList = employeeSessionBean.merge(employeeSessionBean.getEmployeeById(23L));
for (Employee employee : mergedList) {
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}

List<Employee> listAfterRemoving = employeeSessionBean.remove(employeeSessionBean.getEmployeeById(23L));

for (Employee employee : listAfterRemoving) {
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName());
}

关系反面的列表(List<Employee>)会自动反映上述每个操作期间的正确状态。

我目前使用的是 EcliseLink 2.6.0。为什么我看到这样的行为似乎与以下文本不匹配?

As with all bi-directional relationships it is your object model's and application's responsibility to maintain the relationship in both direction. There is no magic in JPA, if you add or remove to one side of the collection, you must also add or remove from the other side, see object corruption. Technically the database will be updated correctly if you only add/remove from the owning side of the relationship, but then your object model will be out of synch, which can cause issues.

http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many

这意味着对于您的特定示例,如果您更改代码以将员工添加到部门(而不是设置部门的其他方式),那么您会注意到这不会自动设置部门在员工身上。您将不得不编写代码来显式执行此操作。

因此,即使您显示的特定代码路径确实有效,也不意味着您可以依赖它。我可以猜猜为什么这样做 - 集合是延迟加载的,并且由于对象在集合加载之前被持久化,所以它能够从数据库中提取正确的数据。

最好的解决方案是听取文档中的建议并在双向关系的两端正确设置状态,尽管有性能方面的考虑(稍后可以微调)。