【问题标题】:JPA Updating Bidirectional AssociationJPA 更新双向关联
【发布时间】:2013-12-02 20:09:52
【问题描述】:

假设我们有以下实体:

    @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 中吗?还是在一个带有分离实体的简单控制器方法上做就足够了?

【问题讨论】:

  • 如果你正在与数据库交互,你应该在一个事务中。无论您是否使用 JPA,这都是正确的。事务是通过调用 EJB 来显式启动还是隐式启动都无关紧要。两者都能胜任。
  • TomAnderson 所说的几乎总是正确的。例外情况是,当您进行查找时,您使用 LockModeType.NONE 以外的锁查找。

标签: java hibernate jpa bidirectional-relation


【解决方案1】:

您有一个 persist 操作(使用em.merge() 制作)。持久化一个新员工意味着部门也被持久化了(你没有级联),所以它会因为另一个原因抛出异常(只需尝试并发布异常)。为了避免这种情况,您要么添加级联类型,要么将它们都持久化(如您在示例中所做的那样)。

关于您的问题:唯一考虑的部分是拥有方。 在 JPA 2.0 规范中,Chapter 3 Entity Operations => 3.2.4 Syncrhonization to the Database 如下:

托管实体之间的双向关系将保持不变 基于关系拥有方持有的引用。它是 开发人员有责任保留内存中的引用 在拥有方和持有在相反方的人一致 当他们改变时彼此。在单向一对一的情况下 和一对多的关系,这是开发者的责任 以确保遵守关系的语义。[29]

与需要事务相关:是的,合并操作需要一个活动事务。摘自 JPA 2 规范:

persist、merge、remove 和 refresh 方法必须在内部调用 当一个实体管理器具有一个事务上下文时 使用事务范围的持久性上下文。如果没有 交易环境, 抛出 javax.persistence.TransactionRequiredException。

关于如何启动事务/如何启动事务:这取决于EntityManager 的类型(取决于获取方式)。在 EJB 中,处理一般情况要容易得多。另外,根据merge method 的文档,会抛出一个TransactionRequiredException,if invoked on a container-managed entity manager of type PersistenceContextType.TRANSACTION and there is no transaction

【讨论】:

    【解决方案2】:

    问题是我是否应该按如下方式在两侧应用合并,并且避免与级联进行第二次合并?

    您可以使用级联注释元素将操作的效果传播到关联实体。级联功能最常用于父子关系。

    如果cascade 元素值cascade=MERGEcascade=ALL 注释已使用cascade 元素值注释这些关系,则merge 操作将级联到由Department 的关系引用的实体。

    托管实体之间的双向关系将根据关系的拥有方(Employee) 持有的引用进行持久化。开发人员有责任保持拥有方 (Employee) 和反面 (Department) 上的内存中引用在更改时彼此保持一致。因此,通过以下一系列语句,关系将通过单个merge 同步到数据库:

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

    这些更改将在事务提交时传播到数据库。通过使用EntityManager#flush 方法,可以在其他时间以及事务处于活动状态时将实体的内存中状态同步到数据库。

    【讨论】:

    • “托管实体之间的双向关系将根据关系的拥有方(员工)持有的引用进行持久化。”这是否意味着您必须合并拥有方(员工)?或者您只是合并要应用级联效果的一侧?
    • 您需要合并要应用级联效果的一侧。您需要合并Department,更改将级联到关联的employees
    • @ChrisGeo:这里有两条单独的规则在起作用。首先,哪些对象被保存到数据库中是由关系上的级联属性决定的。其次,每个对象的哪些状态保存到数据库中由拥有方的对象状态决定。因此,如果您合并Department,并且employees 属性被标记为级联合并,那么Employees 将被合并。如果Employees 状态正确,那么正确的数据将被写入数据库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多