java JPA:一对一关系中的阻抗失配问题
我有一个关于JPA-2.0(提供者是Hibernate)关系及其在Java中的相应管理的问题。假设我有一个部门和一个员工实体:
@Entity
public class Department {
...
@OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
}
@Entity
public class Employee {
...
@ManyToOne(targetEntity = Department.class)
@JoinColumn
private Department department;
...
}
现在我知道我必须自己管理Java关系,就像下面的单元测试一样:
@Transactional
@Test
public void testBoth() {
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
}
如果省略e.setDepartment(d)
或d.getEmployees().add(e)
,断言将失败。到目前为止,一切顺利。如果在两者之间提交数据库事务呢
@Test
public void testBoth() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
em.getTransaction().commit();
em.close();
}
我还需要管理关系的双方吗?不,事实证明,我不必这么做。经过这一修改
e.setDepartment(d);
//d.getEmployees().add(e);
这些断言仍然成功。但是,如果我只设置另一侧:
//e.setDepartment(d);
d.getEmployees().add(e);
断言失败了。为什么?是因为员工是关系的拥有方吗?我可以通过不同的注释来改变这种行为吗?或者,它总是“OneToMany”的“一侧”来确定何时填充数据库中的外键字段
# 1 楼答案
如果只更新前一个上下文中的拥有方,那么在新的持久性上下文中的第二个测试成功的原因是,持久性提供程序显然不知道,在持久化时,您也没有更新相反的方。它只关心拥有方的持久性。但是,当您从持久性提供程序获取持久性对象时,该提供程序会在两侧正确地设置双向关联(只是假设它们也正确地持久化)。然而,正如这里的许多其他人已经指出的,持久性提供者不负责完成新创建的双向关联,您应该始终在代码中正确维护双向关联
# 2 楼答案
我不知道你的测试试图证明什么,但事实是,在处理双向关联时,你必须处理好关联的双方。不这样做是不正确的。句号
更新:虽然axtavt提到的规范参考当然是准确的,但我坚持认为,您肯定必须设置双向关联的双方。不这样做是不正确的,并且在第一个持久性上下文中,实体之间的关联被破坏。这个JPA wiki book是这样说的:
换句话说,在Java中管理双向关联的唯一正确的和安全的方法是设置链接的两侧。这通常是通过防御链接管理方法实现的,比如:
我再说一遍,不这样做是不正确的。您的测试之所以有效,是因为您在一个新的持久性上下文(即非常特殊的情况,而不是一般情况)中访问数据库,但代码在许多其他情况下都会中断
# 3 楼答案
JPA中的实体关系有拥有方和相反方。数据库更新由拥有方的状态决定。在你的例子中
Employee
是一个拥有方,因为mappedBy
属性从JPA 2.0 specification开始: