Cla*_*den 23 java hibernate jpa many-to-one hibernate-cascade
我有两个班
MyItem对象:
@Entity
public class MyItem implements Serializable {
@Id
private Integer id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Component defaultComponent;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Component masterComponent;
//default constructor, getter, setter, equals and hashCode
}
Run Code Online (Sandbox Code Playgroud)
组件对象:
@Entity
public class Component implements Serializable {
@Id
private String name;
//again, default constructor, getter, setter, equals and hashCode
}
Run Code Online (Sandbox Code Playgroud)
我想用以下代码坚持下去:
public class Test {
public static void main(String[] args) {
Component c1 = new Component();
c1.setName("comp");
Component c2 = new Component();
c2.setName("comp");
System.out.println(c1.equals(c2)); //TRUE
MyItem item = new MyItem();
item.setId(5);
item.setDefaultComponent(c1);
item.setMasterComponent(c2);
ItemDAO itemDAO = new ItemDAO();
itemDAO.merge(item);
}
}
Run Code Online (Sandbox Code Playgroud)
虽然这适用于Hibernate 3.6,但Hibernate 4.1.3会抛出
Exception in thread "main" java.lang.IllegalStateException: An entity copy was already assigned to a different entity.
at org.hibernate.event.internal.EventCache.put(EventCache.java:184)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:285)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:896)
at org.hibernate.engine.spi.CascadingAction$6.cascade(CascadingAction.java:288)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:213)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:282)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:904)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:888)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:892)
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:874)
at sandbox.h4bug.Test$GenericDAO.merge(Test.java:79)
at sandbox.h4bug.Test.main(Test.java:25)
Run Code Online (Sandbox Code Playgroud)
数据库后端是h2(但hsqldb或derby也是如此).我究竟做错了什么?
Tob*_*obb 27
我遇到了同样的问题,这就是我发现的:
合并方法遍历您要存储的对象的图形,并且对于此图形中的每个对象,它从数据库加载它,因此它为图形中的每个对象都有一对(持久实体,分离实体),其中分离实体是要存储的实体,持久化实体是从数据库中获取的.(在该方法中,以及在错误消息中,持久性实体被称为"复制").然后将这些对放入两个映射中,一个以持久实体为键,分离实体为值,一个以分离实体为键,持久实体为值.
对于每个这样的entites,它检查这些映射,以查看持久实体是否映射到与之前相同的分离实体(如果它已被访问过),反之亦然.当你得到一对实体,其中使用持久化实体获取get返回值时,会发生此问题,但是从其他映射获取,并且分离的实体返回null,这意味着您已经将持久实体与已分离的实体链接具有不同哈希码的实体(如果您没有覆盖哈希码方法,则基本上是对象标识符).
TL; DR,您有多个具有不同对象标识符/哈希码的对象,但具有相同的持久性标识符(因此引用相同的持久性实体).在较新版本的Hibernate4中显然不再允许这样做(4.1.3.Final和我可以告诉的更高版本).
错误消息不是很好imo,它真正应该说的是:
A persistent entity has already been assigned to a different detached entity
要么
Multiple detached objects corresponding to the same persistent entity
同样在这里,检查你的equals()方法.最有可能实施得很糟糕.
编辑:如果您没有正确实现Entity的equals()和hashCode()方法,我已经验证合并操作不起作用.
您应该遵循以下准则来实现equals()和hashCode():
"建议您使用Business键相等来实现equals()和hashCode().业务键相等性意味着equals()方法仅比较构成业务键的属性.它是一个用于标识我们的实例的键.现实世界(自然候选键)"
这意味着:您不应该将您的Id用作equals()实现的一部分!
尝试在 Component 类中添加@GeneratedValue注释。@Id否则两个不同的实例可能会获得相同的 id,并发生冲突。
看来您给了他们相同的 ID。
Component c1 = new Component();
c1.setName("comp");
Component c2 = new Component();
c2.setName("comp");
Run Code Online (Sandbox Code Playgroud)
这可能会解决你的问题。
| 归档时间: |
|
| 查看次数: |
21813 次 |
| 最近记录: |