试图保存代表同一实体的不同 java 个对象。休眠
Trying to save different java objects which represents the same entity. Hibernate
(这是对真实问题的简化)
让我们从下面的小class开始:
@Entity
class Test {
Test(int id, String name) {
this.id = id;
this.name = name;
}
@Id
private int id;
@Column
private String name;
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Test) {
return id == ((Test) obj).id;
}
return false;
}
}
如果我们执行以下,不会出现异常:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1); // <-- No exception
tA.commit();
我猜第二次调用被忽略了,或者对象又被保存到数据库中了。问题是保存同一个实体两次是没有问题的。现在让我们尝试以下操作:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
Test obj1_ = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1_); // <-- javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session
tA.commit();
什么?对象位于不同的内存位置怎么可能相关?不知何故,代码抛出异常。
如何使第二个示例像第一个示例一样工作?
第一种情况,你保存同一个对象两次,这是允许的。
但在第二种情况下,您将两个不同的对象保存到数据库中,但它们具有相同的主键。这是违反数据库约束。
在第一个示例中,您传递了一个对象的引用以保存它,而在第二个调用中,您传递了完全相同的引用;它们都指向内存中的同一个对象。
但是,在第二个示例中,您使用两个新调用分配了两个对象,这两个调用在两个不同的内存地址创建对象;它们是两个不同的对象。第一个引用指向其他内存地址,然后是第二个对象的引用。如果您在第二个示例中尝试这样做,它将 return false: obj1 == obj1_
我只是重写@jb-nizet 在评论中写的内容,这对我来说感觉像是答案:
Hibernate doesn't use ==. It simply does what you're telling it to do.
persist's contract is: associate this object with the session. If it's
already associated to the session, it's a noop. If it isn't, it is
associated to the session to be inserted in the database later. If
what yo want to do is make sure the state of this object is copied to
a persistent entity, and give me back that persistent entity, then
you're looking for merge().
所以解决方案就是使用
AtheManager.merge(obj1);
而不是
AtheManager.persist(obj1);
(这是对真实问题的简化)
让我们从下面的小class开始:
@Entity
class Test {
Test(int id, String name) {
this.id = id;
this.name = name;
}
@Id
private int id;
@Column
private String name;
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Test) {
return id == ((Test) obj).id;
}
return false;
}
}
如果我们执行以下,不会出现异常:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1); // <-- No exception
tA.commit();
我猜第二次调用被忽略了,或者对象又被保存到数据库中了。问题是保存同一个实体两次是没有问题的。现在让我们尝试以下操作:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("local_h2_persistence");
EntityManager theManager = factory.createEntityManager();
EntityTransaction t = theManager.getTransaction();
Test obj1 = new Test(1, "uno");
Test obj1_ = new Test(1, "uno");
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1_); // <-- javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session
tA.commit();
什么?对象位于不同的内存位置怎么可能相关?不知何故,代码抛出异常。
如何使第二个示例像第一个示例一样工作?
第一种情况,你保存同一个对象两次,这是允许的。 但在第二种情况下,您将两个不同的对象保存到数据库中,但它们具有相同的主键。这是违反数据库约束。
在第一个示例中,您传递了一个对象的引用以保存它,而在第二个调用中,您传递了完全相同的引用;它们都指向内存中的同一个对象。
但是,在第二个示例中,您使用两个新调用分配了两个对象,这两个调用在两个不同的内存地址创建对象;它们是两个不同的对象。第一个引用指向其他内存地址,然后是第二个对象的引用。如果您在第二个示例中尝试这样做,它将 return false: obj1 == obj1_
我只是重写@jb-nizet 在评论中写的内容,这对我来说感觉像是答案:
Hibernate doesn't use ==. It simply does what you're telling it to do. persist's contract is: associate this object with the session. If it's already associated to the session, it's a noop. If it isn't, it is associated to the session to be inserted in the database later. If what yo want to do is make sure the state of this object is copied to a persistent entity, and give me back that persistent entity, then you're looking for merge().
所以解决方案就是使用
AtheManager.merge(obj1);
而不是
AtheManager.persist(obj1);