试图保存代表同一实体的不同java对象。冬眠
(这是对实际问题的简化) 让我们从以下小班开始:试图保存代表同一实体的不同java对象。冬眠,java,hibernate,identity,Java,Hibernate,Identity,(这是对实际问题的简化) 让我们从以下小班开始: @Entity class Test { Test(int id, String name) { this.id = id; this.name = name; } @Id private int id; @Column private String name; @Override
@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工厂=Persistence.createEntityManagerFactory(“本地持久化”);
EntityManager theManager=factory.createEntityManager();
EntityTransaction t=manager.getTransaction();
测试obj1=新测试(1,“uno”);
tA.begin();
AtheManager.persist(obj1);
AtheManager.persist(obj1);// 在第一种情况下,保存同一对象两次,这是允许的。
但在第二种情况下,将两个不同的对象保存到数据库中,但它们都具有相同的主键。这是数据库约束冲突 在第一个示例中,您向一个对象传递一个引用以保存它,在第二个调用中,您传递了完全相同的引用;它们都指向内存中的同一对象
但是,在第二个示例中,您使用两个新调用分配了两个对象,这将在两个不同的内存地址创建对象;它们是两个不同的对象。第一个引用指向另一个内存地址,然后指向第二个对象的引用。如果您在第二个示例中尝试此操作,它将返回false:obj1==obj1我只是在重写@jb nizet在评论中写的内容,这对我来说就像是答案:
Hibernate不使用==。它只是做你让它做的事。
persist的约定是:将此对象与会话关联。如果是
已经关联到会话,这是一个noop。如果不是,那就是
与稍后要插入数据库的会话关联。如果
您要做的是确保将此对象的状态复制到
一个持久性实体,然后将该持久性实体返回给我
您正在查找merge()
所以解决方法就是使用
AtheManager.merge(obj1);
而不是
AtheManager.persist(obj1);
它们有什么“不同”之处?它们代表相同的实体,因此不应违反数据库约束。一次只能有一个给定实体的实例在持久性上下文中。你想做的事毫无意义。假设这是允许的。如果将第一个对象的名称设置为“foo”,将第二个对象的名称设置为“bar”,Hibernate应该怎么做?它应该将foo或bar写入数据库吗?obj1
和obj1
是不同的Java对象-因此Hibernate认为它们应该在数据库表中创建两个不同的行-但这是不可能的,因为不可能有两个主键相等的行。@JBNizet obj1.equals(obj_1)=true,所以没有问题。如果名称不同,则obj1.equals(obj1_)=false。为什么Hibernate使用引用相等而不是值相等?Hibernate不使用==。它只是做你让它做的事。persist的约定是:将此对象与会话关联。如果它已经与会话关联,则它是一个noop。如果不是,它将与稍后插入数据库的会话相关联。如果您想做的是确保将此对象的状态复制到一个持久实体,并将该持久实体返回给我,那么您正在寻找merge()。我在问题中已经指出了这一点。问题是,为什么Hibernate使用引用相等而不是值相等,以及如何使它使用值相等?我猜它使用引用检查,因为这样可以保证值相等。记住,客户机代码可以自由覆盖equals;它的实现可以是始终返回true。执行内存位置检查更安全。这在equals实现中是一个问题,因此在这种情况下,未定义的行为是可以的。无论如何,难道没有办法实现我想要的吗?@JoséD。这样的“错误”会影响实体管理器的状态,这就是为什么它的设计者决定进行内存地址检查。尝试合并方法?我并不熟悉这个接口,但是从我从文档中读到的内容来看,您应该使用merge方法。
AtheManager.persist(obj1);