JPA - 删除双向关系的元素

时间:2010-08-12 18:32:48

标签: orm jpa relationship jpa-2.0 bidirectional

为什么我可以删除双向关系的元素,尽管在持久化上下文中只管理关系的一侧(例I)?当我的单向关系不起作用时(参见例II)。为什么呢?

实体:

@Entity
Class User {
    ...
    @OneToMany(mappedBy = "user")
    private List<Process> processes;

    @OneToOne // Unidirectional
    private C c;
    ...

    @PreRemove
    private void preRemove() {
        for (Process p : processes) {
            p.internalSetUser(null);
        }
    }
   ...
}

@Entity
Class Process {
    ...
    @ManyToOne
    private User user;
    ...

    @PreRemove
    protected void preRemove() {
        if (this.user != null) {
            user.internalRemoveProcess(this);
        }
    }
   ...
}

@Entity
Class C {

 }

示例I:

// Create User u1 with Processes p1, p2

tx.start();
// Only u1 is manged in persistence context and no process
userFacade.delete(u1); // There following is called: >> em.remove(em.merge(u1)); // Works
tx.commit();

示例II:

// Create User u and Object C c, establish their relation.

tx.start();
cFacade.remove(c); //>>MySQLIntegrityConstraintViolationException,foreign key constraint fails
ty.commit();

在第一个例子中,我使用这些内部方法在每种情况下设置关系的另一面但是另一边不在持久化上下文中管理我认为?!当我更改用户的进程并保存用户时,除非我使用cascade.MERGE或者如果两者都在事务中加载并且因此在pc中管理,否则不会更新该进程。那么为什么删除工作呢?

2 个答案:

答案 0 :(得分:1)

在示例II中,我猜您必须在删除user.setC(null)之前致电c

在例子I中,这是我的理解。您首先合并u1,以便将u1'加载到PC中,并将状态u1复制到u1'(这就是全部,因为您没有级联{{ 1}}),然后返回。然后,您调用删除(MERGE),u1'被调用并更改preRemovep1'。因此它们很脏并且会在刷新时进行适当更新(将FK设置为​​p2'),而NULL将被删除。一切正常。

以防万一,以下是JPA 2.0规范中合并操作的语义:

  

3.2.7.1合并分离的实体状态

     

合并操作允许   从分离的国家传播   实体到持久化实体   由实体经理管理。

     

合并操作的语义   应用于实体X如下:

     
      
  • 如果u1'是一个分离的实体,则X的状态将复制到一个   预先存在的托管实体实例   X具有相同身份或新身份   已创建X'的托管副本X'
  •   
  • 如果X是新实体实例,则新的托管实体实例X是   创建并复制X'的状态   进入新的托管实体实例   X
  •   
  • 如果X'是已删除的实体实例,则会X   由合并操作抛出(或者   事务提交将失败)。
  •   
  • 如果IllegalArgumentException是托管实体,则合并操作会忽略它,   但是,合并操作是   级联到由...引用的实体   来自X的关系,如果这些   关系已被注释   级联元素值   Xcascade=MERGE   注释
  •   
  • 对于来自cascade=ALL的关系所引用的所有实体Y   级联元素值X   或cascade=MERGEcascade=ALL已合并   递归地为Y。对于所有这样的Y'   由Y引用,X设置为   参考X'。 (请注意,如果Y'是   托管然后X是与...相同的对象   X。)
  •   
  • 如果X'是合并到X的实体,则引用另一个实体   X',其中Y或   那么,未指定cascade=MERGE   导航同一个协会   来自cascade=ALL产生对a的引用   托管对象X'具有相同的   持久性身份为Y'
  •   
     

持久性提供程序不能   合并标记为LAZY的字段没有   被抓取:它必须忽略这样   合并时的字段。

     

使用的任何Y列   必须通过实体检查实体   持久性运行时实现   在合并操作期间和/或在   刷新或提交时间。离席期间   Version列中没有   其他版本检查由   持久性提供程序运行时   合并期间。

参考

  • JPA 2.0规范
    • 3.2.7.1合并分离的实体状态

答案 1 :(得分:1)

这是预期的行为:

  

由于关系是双向的,因此应用程序更新   关系的一方,另一方也应该更新,   并保持同步。在JPA中,就像在Java中一般一样   应用程序的责任,或维护的对象模型   关系。如果你的应用程序添加到一个   关系,然后它必须添加到另一方。

     

这可以通过对象模型中的add或set方法来解决   处理关系的两面,所以应用程序代码   不需要担心它。有两种方法可以解决这个问题,   您只能将关系维护代码添加到一侧   关系,只从一侧使用setter(如   使另一方受到保护),或将其添加到双方并确保   你避免无限循环。

例如:

public class Employee {
    private List phones;
    ...
    public void addPhone(Phone phone) {
        this.phones.add(phone);
        if (phone.getOwner() != this) {
            phone.setOwner(this);
        }
    }
    ...
}

来源:OneToMany#Getters_and_Setters