wal*_*orn 4 java orm jpa jpa-2.0
我有一个关于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;
...
}
Run Code Online (Sandbox Code Playgroud)
现在我知道我必须自己管理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());
}
Run Code Online (Sandbox Code Playgroud)
如果我遗漏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();
}
Run Code Online (Sandbox Code Playgroud)
我还需要管理关系的两个方面吗?不,事实证明,我没有必要.有了这个修改
e.setDepartment(d);
//d.getEmployees().add(e);
Run Code Online (Sandbox Code Playgroud)
断言仍然成功.但是,如果我只设置另一方:
//e.setDepartment(d);
d.getEmployees().add(e);
Run Code Online (Sandbox Code Playgroud)
断言失败了.为什么?是因为员工是关系的拥有者吗?我可以通过不同的注释来改变这种行为吗?或者它是否始终是"OneToMany"的"一"侧确定何时填充数据库中的外键字段?
我不知道你的测试试图证明什么,但事实是你在处理双向关联时必须处理关联的两个方面.不这样做是不正确的.期.
更新:虽然axtavt提到的规范参考当然是准确的,但我坚持认为,你必须设置双向关联的两面.不这样做是不正确的,并且第一个持久化上下文中的实体之间的关联被破坏.在JPA维基书中所说的那样是这样的:
与所有双向关系一样,您的对象模型和应用程序负责维护双向关系.JPA中没有任何魔力,如果您在集合的一侧添加或删除,您还必须在另一侧添加或删除,请参阅对象损坏.从技术上讲,如果您只是从关系的拥有方添加/删除数据库,那么数据库将会正确更新,但随后您的对象模型将不同步,这可能会导致问题.
换句话说,在Java中管理双向关联的唯一正确和安全的方法是设置链接的两侧.这通常使用防御性链接管理方法完成,如下所示:
@Entity
public class Department {
...
@OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
public void addToEmployees(Employee employee) {
this.employees.add(employee);
employee.setDepartment(this);
}
}
Run Code Online (Sandbox Code Playgroud)
我再说一遍,不这样做是不正确的.您的测试只能起作用,因为您在新的持久化上下文(即非常特殊的情况,而不是一般情况)中访问数据库,但代码会在许多其他情况下中断.
JPA中的实体关系具有拥有和反向.数据库更新由拥有方的状态决定.在你的情况下,Employee由于mappedBy属性是一个拥有方.
2.9实体关系
...
关系可以是双向的或单向的.双向关系具有拥有方和反向(非拥有方).单向关系只有一个拥有方.关系的拥有方确定数据库中关系的更新,如3.2.4节所述.
以下规则适用于双向关系:
- 双向关系的反面必须通过使用OneToOne,OneToMany或ManyToMany批注的mappedBy元素来引用其拥有方.mappedBy元素指定作为关系所有者的实体中的属性或字段.
- 一对多/多对一双向关系的许多方面必须是拥有方,因此不能在ManyToOne批注上指定mappedBy元素.
- 对于一对一的双向关系,拥有方对应于包含相应外键的一侧.
- 对于多对多双向关系,任何一方都可能是拥有方.
| 归档时间: |
|
| 查看次数: |
1121 次 |
| 最近记录: |