Orm JPA-删除双向关系的元素
为什么我可以删除双向关系的元素,尽管在持久性上下文中只管理关系的一侧(示例i)?当我的单向关系不起作用时(参见示例II)。为什么? 实体: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
@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
会加载到您的预删除中(并因此变得真正受管理)。