试图保存代表同一实体的不同 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);