为什么我可以删除双向关系的元素,尽管在持久化上下文中只管理关系的一侧(例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中管理,否则不会更新该进程。那么为什么删除工作呢?
答案 0 :(得分:1)
在示例II中,我猜您必须在删除user.setC(null)
之前致电c
。
在例子I中,这是我的理解。您首先合并u1
,以便将u1'
加载到PC中,并将状态u1
复制到u1'
(这就是全部,因为您没有级联{{ 1}}),然后返回。然后,您调用删除(MERGE
),u1'
被调用并更改preRemove
和p1'
。因此它们很脏并且会在刷新时进行适当更新(将FK设置为p2'
),而NULL
将被删除。一切正常。
以防万一,以下是JPA 2.0规范中合并操作的语义:
3.2.7.1合并分离的实体状态
合并操作允许 从分离的国家传播 实体到持久化实体 由实体经理管理。
合并操作的语义 应用于实体X如下:
- 如果
u1'
是一个分离的实体,则X
的状态将复制到一个 预先存在的托管实体实例X
具有相同身份或新身份 已创建X'
的托管副本X'
。- 如果
X
是新实体实例,则新的托管实体实例X
是 创建并复制X'
的状态 进入新的托管实体实例X
。- 如果
X'
是已删除的实体实例,则会X
由合并操作抛出(或者 事务提交将失败)。- 如果
IllegalArgumentException
是托管实体,则合并操作会忽略它, 但是,合并操作是 级联到由...引用的实体 来自X
的关系,如果这些 关系已被注释 级联元素值X
或cascade=MERGE
注释- 对于来自
cascade=ALL
的关系所引用的所有实体Y
级联元素值X
或cascade=MERGE
,cascade=ALL
已合并 递归地为Y
。对于所有这样的Y'
由Y
引用,X
设置为 参考X'
。 (请注意,如果Y'
是 托管然后X
是与...相同的对象X
。)- 如果
X'
是合并到X
的实体,则引用另一个实体X'
,其中Y
或 那么,未指定cascade=MERGE
导航同一个协会 来自cascade=ALL
产生对a的引用 托管对象X'
具有相同的 持久性身份为Y'
。持久性提供程序不能 合并标记为LAZY的字段没有 被抓取:它必须忽略这样 合并时的字段。
使用的任何
Y
列 必须通过实体检查实体 持久性运行时实现 在合并操作期间和/或在 刷新或提交时间。离席期间Version
列中没有 其他版本检查由 持久性提供程序运行时 合并期间。
答案 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);
}
}
...
}