我有以下问题.我有3个实体,我正在使用OneToOne单向:
ENTITY1
@Entity
public class Entity1 implements Serializable{
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
Long id;
String name;
String value;
}
Run Code Online (Sandbox Code Playgroud)
ENTITY2
@Entity
public class Entity2 implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@OneToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST})
Entity1 entity1;
public Entity1 getEntity1() {
return entity1;
}
String name;
String value;
}
Run Code Online (Sandbox Code Playgroud)
ENTITY3
@Entity
public class Entity3 implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Long id;
@OneToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST})
private Entity1 entity1;
public Entity1 getEntity1() {
return entity1;
}
public void setEntity1(Entity1 entity1) {
this.entity1 = entity1;
}
String name;
String value;
}
Run Code Online (Sandbox Code Playgroud)
一个小测试:
public void testApp()
{
EntityManager em = TestHibernateUtil.getEntityManager();
em.getTransaction().begin();
Entity1 entity1 = new Entity1();
entity1.name = "Name1";
entity1.value = "Value1";
Entity2 entity2 = new Entity2();
entity2.name = "Name2";
entity2.value = "Value2";
entity2.setEntity1(entity1);
**em.merge(entity2);**// if change that to persist - I get one Entity1
Entity3 entity3 = new Entity3();
entity3.name = "Name3";
entity3.value = "Value3";
entity3.setEntity1(entity1);
**em.merge(entity3);** // if change that to persist - I get one Entity1
em.getTransaction().commit();
}
Run Code Online (Sandbox Code Playgroud)
所以看看上面的测试,如果我使用em.merge我在事务提交后在持久化上下文中获得Entity1的2个实体,如果我将其更改为em.persist然后我在持久化上下文中获得Entity1的一个实体.任何人都可以解释为什么会发生这种情况或指向一些文档吗?
Dan*_*yMo 24
您看到的行为是两件事的结果:
em.merge(x)不会成为x托管对象,而是调用em.persist(x)will)em.merge() 撞倒:em.merge(entity2); 被调用
entity1entity1被复制到新的托管实例中,但不是自己管理的em.merge(entity3); 被调用
entity1再次级联entity1仍然是非托管的并且没有标识符,所以它无法与先前合并创建的现有托管实例匹配.结果是创建了另一个新实例entity1存在3个实例.合并操作和初始非托管实例创建的两个托管实例请注意,如果您的实体具有显式ID,则第二个合并将不会创建新实例,而是会复制entity1到已存在的托管实例中.此外,如果您尝试合并已托管的实例,则第二次合并操作将被忽略.
em.persist() 撞倒:em.persist(entity2); 被调用
entity1entity1 现在是一个托管对象em.persist(entity3); 被调用
entity1再次级联entity1已经管理,因此将忽略持久操作entity1存在且被管理.entity1 保存在数据库中此行为在JPA 2.0规范部分3.2.7.1合并分离的实体状态中定义:
应用于实体X的合并操作的语义如下:
- 如果X是新的实体实例,则创建新的管理实体实例X',并且将X的状态复制到新的管理实体实例X'中.
- 对于具有级联元素值cascade = MERGE或cascade = ALL的X的关系所引用的所有实体Y,Y被递归地合并为Y'.对于由X引用的所有这样的Y,X'被设置为引用Y'.(注意,如果管理X,那么X与X'是同一个对象.)
- [...]
和第3.2.2节持久性和实体实例:
应用于实体X的持久化操作的语义如下:
- 如果X是一个新实体,它就会被管理.实体X将在事务提交时或之前或作为刷新操作的结果输入数据库.
- 如果X是预先存在的托管实体,则persist操作会忽略它.[...]
- [...]
另请参阅:JPA何时设置@GeneratedValue @Id)
Ato*_*tom 14
我不会梦想挑战DannyMo的超级答案,但我想补充一下:
Persist和merge旨在保留某个对象的托管实例.
如果使用persist,则表示该对象尚不存在,因此将其作为唯一的托管实例不会造成伤害.
使用合并时,考虑到对象的托管实例可能已存在.您不想替换该唯一的托管实例,因为其他一些对象可能会引用它,认为它是托管对象.
如果要在合并后对对象进行操作,则正确的合并将如下所示:
managedObject = em.merge(object); // or just object = em.merge(object)
//You cannot do it with persist since it returns null
如果您想检查managedObject并object指向同一个对象实例检查if(managedObject == object),你会得到错误的(真有可能,当您使用合并上已经被管理对象和操作被忽略).
如果你在过时版本的对象上使用merge,你将它作为参数传递给上一个合并,jpa不知道如何找到正确的对象,因为它还没有id.假设它是一个新对象,将创建新的托管实例.
我是个新秀.如果我在任何地方都错了,请纠正我.
| 归档时间: |
|
| 查看次数: |
24230 次 |
| 最近记录: |