JPA更新双向关联

13

假设我们有以下实体:

    @Entity
    public class Department {

        @OneToMany(mappedBy="department")
        private List<Employee> employees;
    }

    @Entity
    public class Employee {

        @ManyToOne
        private Department department
    }

在更新时,我们需要维护以下关系的两个方面是可以理解的:

Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);

到目前为止一切都很好。问题是我是否应该按以下方式在两侧应用合并,并且我能否避免使用级联的第二次合并?

entityManager.merge(emp);
entityManager.merge(dep);

合并拥有方是否足够?这些合并操作应该在事务或EJB中进行吗?还是在简单的控制器方法中使用分离实体就足够了?


1
如果你正在与数据库交互,那么你应该在一个事务中。这是无论你是否使用JPA都是正确的。无论事务是显式还是隐式地由对EJB的调用启动,都不重要;两者都能完成工作。 - Tom Anderson
TomAnderson所说的几乎总是正确的。例外情况是当您使用除LockModeType.NONE之外的锁进行查找时。 - V G
2个回答

14
问题是我是否应该按照以下方式在双方都应用合并,并且我可以避免使用级联进行第二次合并吗?
您可以使用级联注释元素将操作的效果传播到相关实体。级联功能最常用于父子关系中。
如果已使用级联元素值cascade = MERGE或cascade = ALL注释, Department 引用的与关系相关的实体将级联执行合并操作。
托管实体之间的双向关系将基于由关系所属方(员工)持有的引用进行持久化。当它们更改时,开发人员有责任使所持有的关系一侧(员工)和所持有的反向一侧(部门)的内存引用相互一致。因此,通过下面的一系列语句,关系将使用单个 merge 同步到数据库:
Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);
...
entityManager.merge(dep);
这些更改将在事务提交时传播到数据库。当事务处于活动状态时,也可以使用EntityManager#flush方法将实体的内存状态与数据库同步。

托管实体之间的双向关系将基于拥有关系的一方(员工)所持有的引用进行持久化。这是否意味着您必须合并拥有关系的一方(员工)?或者您只需合并要应用级联效果的那一方? - ChrisGeo
你需要合并你想要应用级联效果的一侧。你需要合并Department,并且更改将级联到相关的employees上。 - Debojit Saikia
6
这里涉及到两个不同的规则。首先,决定哪些对象保存到数据库中是由关系级联属性确定的。其次,每个对象在所有者方面的状态决定了将保存哪些状态到数据库中。因此,如果您合并了“Department”,并且“employees”属性被标记为级联合并,则“Employee”将被合并。如果“Employee”的状态正确,则正确的数据将被写入数据库中。 - Tom Anderson

3
您在这里进行了一个持久化操作(使用em.merge())。持久化新员工并不意味着部门也被持久化(您没有级联),因此它将因另一个原因而抛出异常(请尝试并发布异常)。为了避免这种情况,您可以添加级联类型或同时持久化它们(就像您在示例中所做的那样)。
关于您的问题:唯一考虑的部分是拥有方。在JPA 2.0规范中,第3章实体操作 => 3.2.4与数据库同步如下:
“托管实体之间的双向关系将基于拥有方保存的引用进行持久化。当它们改变时,开发人员有责任使拥有方和反向方上保留的内存引用彼此一致。对于单向一对一和一对多关系,开发人员有责任确保遵守关系的语义。” 关于需要事务的需求:是的,您需要一个活动事务来进行合并操作。摘自JPA 2规范:
“当使用具有事务作用域持久性上下文的实体管理器时,必须在事务上下文中调用persist、merge、remove和refresh方法。如果没有事务上下文,则会抛出javax.persistence.TransactionRequiredException。”
关于如何启动事务/如何启动事务:它取决于EntityManager的类型(您获取它的方式)。在EJB中,更容易处理通用情况。此外,根据合并方法的文档,如果在没有事务的情况下在类型为PersistenceContextType.TRANSACTION的容器管理实体管理器上调用该方法,则会抛出TransactionRequiredException。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接