Orm JPA-删除双向关系的元素

Orm JPA-删除双向关系的元素,orm,jpa,relationship,jpa-2.0,bidirectional,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

为什么我可以删除双向关系的元素,尽管在持久性上下文中只管理关系的一侧(示例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 {

 }
// 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();
// Create User u and Object C c, establish their relation.

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

@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 {

 }
// 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();
// 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中,我想在删除
c
之前,您必须调用
user.setC(null)

在我的例子中,这是我的理解。您首先合并
u1
,因此将
u1'
加载到PC中,
u1
的状态复制到
u1'
(这是因为您没有级联
MERGE
),然后返回。然后调用remove(在
u1'
上),调用
preRemove
并更改
p1'
p2'
。因此,它们是脏的,在刷新时会得到适当的更新(将FK设置为
NULL
),而
u1'
将被删除。一切正常

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

3.2.7.1合并分离实体状态 合并操作允许 分离态的传播 将实体转换为持久实体 由实体经理管理

合并操作的语义 适用于实体X的参数如下:

  • 如果
    X
    是分离的实体,则将
    X
    的状态复制到 预先存在的托管实体实例 相同身份或新身份的
    X'
    已创建
    X
    的托管副本
    X'
  • 如果
    X
    是一个新的实体实例,则将创建一个新的托管实体实例
    X'
    创建并复制
    X
    的状态 进入新的托管实体实例
    X'
  • 如果
    X
    是一个已删除的实体实例,则会出现
    IllegalArgumentException
    由合并操作(或 事务提交将失败)
  • 如果
    X
    是托管实体,则合并操作将忽略该实体, 但是,合并操作是无效的 级联到引用的实体 来自
    X
    的关系,如果 已使用注释对关系进行了注释 级联元素值
    cascade=MERGE
    cascade=ALL
    注释
  • 对于由具有 级联元素值
    cascade=MERGE
    cascade=ALL
    Y
    被合并 递归为
    Y'
    。对于所有此类
    Y
    X
    引用,
    X'
    设置为 参考
    Y'
    。(请注意,如果
    X
    为 然后管理的
    X
    X'
  • 如果
    X
    是一个合并到
    X'
    的实体,并引用另一个实体
    Y
    ,其中
    cascade=MERGE
    cascade=ALL
    未指定,则 同一协会的导航 从
    X'
    生成对 管理对象
    Y'
    具有相同的 持久标识为
    Y
持久性提供程序不能 合并标记为惰性的字段,但这些字段尚未 已获取:它必须忽略此类 合并时的字段

用户使用的任何
版本
列 实体必须由 持久性运行时实现 在合并操作期间和/或在 刷新或提交时间。缺席 在
版本
列中没有 由执行的附加版本检查 持久性提供程序运行时 在合并操作期间

参考文献
  • JPA2.0规范
    • 3.2.7.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);
              }
          }
          ...
      }
      

      来源:

      谢谢帕斯卡!我认为在这两种情况下你都是对的。例如,我是上述规范的最后一点。如果我在示例中使用,我不会合并以下内容:“User u1Managed=em.find(u1.getId());em.remove(u1Managed);”这意味着find操作也会产生一个受管理的p1'和p2'?@Michael
      进程
      是懒惰的,但我认为
      p1
      p2
      会加载到您的
      预删除中(并因此变得真正受管理)。