休眠:实体副本已分配给不同的实体

时间:2013-07-13 21:06:16

标签: hibernate merge

HiAll,

我目前正在尝试了解,我的编码和实体模型有什么问题 我使用hibernate 4.2进行CRUD操作,只使用Generic DAO模式和带有级联ALL和orphanRemoval = true的带注释实体。 我有与 DayPlanElement 弱实体的OneToMany关系的 DayPlan 实体。 DayPlanElement 保存在java.util.Set中。每个 DayPlanElement 都有一个属性“order”。

DayPlan 与实体 Person 之间也有OneToMany关系。实体 Person 保存在java.util。列表中。

DayPlanElement DayPlanElementEntry 弱实体具有OneToMany关系。 java.util.Set用于保存。 Person 实体也与 DayPlanElementEntry 弱实体的OneToMany关系。 java.util.Set用于保存。 DayPlan DayPlanElement Person 实体拥有由我的应用程序管理的ID作为String。 DayPlanElementEntry 弱实体具有使用EmbeddedId注释的复合Id:DayPlanElementEntryId,包含parentPersonId和dayPlanElementId。它

换句话说,想象一下,存在一个表,代表日计划。列是从0到24的小时数。行是人员,必须从日计划开始操作。每列都是DayPlanElement实体。每一行都是Person实体。每个单元格都是DayPlanElementEntry实体。

如果我只是添加到表新元素(也是人)并删除它(也从列表中删除它然后调用DayPlanDAO.merge(dayPlan) - 我希望在级联和orphanRemoval) - 我没有问题。

只有当我尝试重新排序给定的Persons(只是删除java.util.List中的操作)和调用DayPlanDAO.merge(dayPlan)时,才会抛出以下异常:

Caused by: java.lang.IllegalStateException: 
Error occurred while storing entity 
[DayPlanElementEntry [getDayPlanMode()=NONE, getCompositeId()=DayPlanElementEntryId [parentPersonId=874c8eac-8796-478d-a4d5-dd011f7d6a4b, dayPlanElementId=ab683a25-633e-419e-89b6-4aef7829d4f6], hashCode=-2039940039]]. 

An entity copy 
[org.hw.domain.DayPlanElementEntry#DayPlanElementEntryId [parentPersonId=874c8eac-8796-478d-a4d5-dd011f7d6a4b, dayPlanElementId=ab683a25-633e-419e-89b6-4aef7829d4f6]] 

was already assigned to a different entity 

[org.hw.domain.DayPlanElementEntry#DayPlanElementEntryId [parentPersonId=874c8eac-8796-478d-a4d5-dd011f7d6a4b, dayPlanElementId=ab683a25-633e-419e-89b6-4aef7829d4f6]].

       at org.hibernate.event.internal.EventCache.put(EventCache.java:192)

       at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:285)

       at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151)

我去处理EventCache,找到问题解决方案,只检查实现的equals()和hashCode()方法 - 没有成功: - (( 注意,我使用equals()方法Id。例如,DayPlanElementEntry的复合ID。该id由hashCode()使用。我只是不明白错误信息,三次输出相同的属性 - ids!

我找到了事件缓存的test class,但现在我不明白以下用例测试:

    @Test
public void testCopyAssociatedWith2ExistingEntities() {
session.getTransaction().begin();
Simple entity1 = new Simple( 1 );
session.persist( entity1 );
Simple copy1 = new Simple( 1 );
cache.put(entity1, copy1);
Simple entity2 = new Simple( 2 );
session.persist( entity2 );
Simple copy2 = new Simple( 2 );
cache.put( entity2, copy2 );
session.flush();

try {
cache.put( entity1, copy2 );
fail( "should have thrown IllegalStateException");
}
catch( IllegalStateException ex ) {
// expected
assertTrue( ex.getMessage().startsWith( "Error occurred while storing entity [org.hibernate.event.internal.EventCacheTest$Simple#1]." ) );
}
session.getTransaction().rollback();
}

...

    @Entity
private static class Simple {
@Id
private int value;

public Simple(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public void setValue(int value) {
this.value = value;
}
}

我们有 Id = 1 的简单实体entity1,然后我们坚持它。然后我们创建Simple实体copy1并将其作为值放入事件缓存中。使用持久化实体* 1 *作为关键。 然后我们使用 Id = 2 创建一个简单实体* 2 *,保留它,然后创建ID为2的副本* 2 *实体并将其作为值放入事件缓存中。使用持久性 entity2 作为关键。  为什么这种情况是错误的,为什么会出现这种情况,必须抛出IllegalStateException ??

感谢您的帮助!

6 个答案:

答案 0 :(得分:10)

对于Hibernate版本4.3.4及其周围,有一个修复。只需添加此工件:

<dependency>
    <groupId>com.intersult</groupId>
    <artifactId>jpa-fix</artifactId>
    <version>1.1</version>
</dependency>

此修补程序将MergeEventListener替换为处理此类合并的MergeEventListener。

答案 1 :(得分:6)

我的代码遇到了同样的问题。试试这个解决方案:

这可能是由两个“cascade.all”引起的。当使用DayPlan实体时,它将同时具有列表和列表,并且两者都具有1:M到DayPlanElementEntry。

因此,当DayPlanElement尝试访问时,两个“cascade.all”可能会发现DayPlanElementEntry已分配给Person,即使它与DayPlanElementEntry相同。

我希望它有所帮助。

最佳, 冲

答案 2 :(得分:6)

可以通过升级到hibernate 4.2.15 / 4.3.6或更高版本并将以下行添加到persistence.xml来重新解析:

<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>

有关详细信息,请参阅https://hibernate.atlassian.net/browse/HHH-9106

答案 3 :(得分:1)

如果这是在测试的上下文中,我建议您使用session.clear()跟随session.flush()。这将有效地清除您的会话缓存,这似乎是问题的核心。

这个问题的潜在原因(我能想到):

  • 将相同的实体副本添加到不同实体键下的缓存中。在EventCache.java中的put方法下设置断点以获取更多详细信息。
  • 您正在使用的hibernate版本中新引入的错误。

答案 4 :(得分:0)

项目和组件之间的关系是单向还是双向?如果它是双向的,请确保没有Cascade.MERGE调用返回到Item。

从您的关系中删除CascadeType.Merge

答案 5 :(得分:0)

在jpa配置文件中添加以下内容将解决问题。

在多对多关系中,如果同一实体在执行与已存在实体的合并操作时重复,则hibernate将无法区分它们,直到我们帮助他获得差异。

properties.put("hibernate.event.merge.entity_copy_observer", "allow");