2

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

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

4

2 回答 2

1

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

在示例 I 中,这是我的理解。您首先合并u1,因此 au1'被加载到 PC 中,并且状态u1被复制到u1'(这就是因为您没有级联MERGE),然后返回。然后你调用 remove (on u1'),preRemove被调用并更改p1'and 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=MERGEcascade=ALL 注释进行注释,则合并操作将级联到关系引​​用的实体。
  • 对于由具有级联元素值 或Y的关系引用的所有实体,递归地合并为。对于所有此类 引用的,设置为引用。(请注意,如果是托管的,那么是与 相同的对象 。)Xcascade=MERGEcascade=ALLYY'YXX'Y'XXX'
  • 如果X一个实体合并到X',并引用另一个实体 Y,其中cascade=MERGE或未 cascade=ALL指定,则从相同关联的导航产生对具有相同持久标识X'的托管对象的引用。Y'Y

持久性提供程序不得合并标记为 LAZY 且尚未获取的字段:合并时必须忽略此类字段。

实体使用的任何Version列都必须在合并操作期间和/或在刷新或提交时由持久性运行时实现检查。在没有Version列的情况下,持久性提供程序运行时不会在合并操作期间进行额外的版本检查。

参考

  • JPA 2.0 规范
    • 3.2.7.1 合并分离实体状态
于 2010-08-12T23:06:57.953 回答
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

于 2012-08-24T23:37:58.010 回答