IllegalStateException异常与Hibernate 4和多对一级联IllegalSt

2019-05-14 00:49发布

我有这两个类

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
}

组件对象:

@Entity
public class Component implements Serializable {

    @Id
    private String name;

    //again, default constructor, getter, setter, equals and hashCode
}

而且我特林坚持那些下面的代码:

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);
    }
}

虽然这正常工作与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)

数据库后端是H2(但HSQLDB或德比同样的情况)。 我究竟做错了什么?

Answer 1:

我有同样的问题,这是我发现:

合并方法遍历要存储对象的图形,并在此图中的每个对象加载它从数据库中,所以它为每个对象对(持久化实体,独立实体)中的图表,其中超脱实体是将被存储在实体和持久性实体从数据库得到。 (在该方法中,以及在错误消息中的持久性实体被称为“复制”)。 然后,这些对被投了两个图,一个与持久实体键和脱离实体价值,另一个是分离的实体键和持久的实体价值。

对于每个这样的一对的entites的,它会检查这些映射中,以查看是否持久实体之前映射到同一个分离的实体(如果已经被访问过的话),并且反之亦然。 当你得到一个对,其中做了获得与持久性实体返回一个值的实体,而是从其他地图上得到的,与分离的实体返回null,这意味着你已经链接持久实体与分离,就会出现此问题实体与不同的散列码(基本上如果尚未重写的哈希码-方法的对象标识符)。

TL; DR,您有不同的对象标识符/哈希码的多个对象,但具有相同的持久标识符(因而引用相同的持久性实体)。 这是appearantly不再Hibernate4的新版本(4.1.3.Final及以上的,从我可以告诉)允许的。

该错误消息不是伊莫非常好,它真的应该说的是一样的东西:

A persistent entity has already been assigned to a different detached entity

要么

Multiple detached objects corresponding to the same persistent entity



Answer 2:

同样在这里,请检查您的equals()方法。 最有可能被严重实现。

编辑:我已经验证,如果不正确地执行你的实体的equals()和hashCode()方法合并操作将无法正常工作。

您应该遵循实现equals()和hashCode()下列原则:

http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch04.html#persistent-classes-equalshashcode

“建议你实现equals()和hashCode()使用业务键值相等。业务键值相等的意思是,equals()方法仅仅比较形成业务键的属性,它是将确定在我们的实例中的关键现实世界(自然的候选键)”

这意味着:你不应该使用您的ID为您的equals()方法实现的一部分!



Answer 3:

为项目和组件的单向双向之间的关系如何? 如果它是双向的,确保你没有Cascade.MERGE调用回上去项。

基本上,Hibernate的较新版本有一个包含了需要根据呼叫合并被合并所有的事情的清单(),它会调用合并,然后移动到下一个,但保持对事物的地图上一个实体的地图,它会引发你的国家,上述错误“实体拷贝已分配给不同的实体”,当它遇到已经处理了一个项目。 我们在应用中发现,当我们位于这“向上”合并对象图中即。 在双向链接,它解决了这一合并的呼叫。



Answer 4:

有相同的异常(休眠4.3.0.CR2)累人保存它有子对象的两个副本,一个对象得到了由固定的,从实体:

@OneToOne(cascade = CascadeType.MERGE)
private User reporter;
@OneToOne(cascade = CascadeType.MERGE)
private User assignedto;

只是,

@OneToOne
private User reporter;
@OneToOne
private User assignedto;

我不知道原因,虽然



Answer 5:

尝试添加@GeneratedValue下注解@Id在组件类。 另有两种不同情况下可能会得到相同的id和碰撞。

它接缝你给他们相同的ID。

    Component c1 = new Component();
    c1.setName("comp");
    Component c2 = new Component();
    c2.setName("comp");

这只是可能会解决问题找你。



Answer 6:

如果名称是ID你为什么要创建两个对象具有相同的ID? 你可以在所有的代码中使用的C1对象。

如果这只是一个例子,你在代码中创建的另一部分C2对象,那么你不应该创建一个新的对象,但是从数据库加载它:

c2 = itemDao.find("comp", Component.class); //or something like this AFTER the c1 has been persisted


Answer 7:

据EventCache逻辑对象图中的所有实体应该是唯一的。 所以,最好的解决办法(或者是解决?)是在MyItem删除级联组件。 并分别合并组件,如果它真的需要 - 我敢打赌,案件组件的95%,不应该根据业务逻辑被合并。

从另一方面 - 我真的很想知道这个限制背后的真实想法。



Answer 8:

如果你正在使用JBoss EAP 6 ..改到JBoss 7.1.1。这是JBoss的EAP 6的缺陷https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.3/html/6.3。 0_Release_Notes / ar01s07s03.html



Answer 9:

我有同样的问题,只是解决了这个问题。 虽然上述答案可以解决我跟几个同事不同意的问题,尤其是改变实施equlas()和hashCode()方法。 但是我觉得我的回答加强@Tobb和@Supun S'的答案(S)。

在我的许多方面(子边)我有

 @OneToMany(mappedBy = "authorID", cascade =CascadeType.ALL, fetch=FetchType.EAGER)
 private Colllection books;

而在我的一侧(父方)

 @ManyToOne(cascade =CascadeType.ALL)
 private AuthorID authorID;

阅读由@Tobb提供出色的最多的回答和思考的一点点,我意识到注释没有什么意义。 我的理解是(对我来说)的办法,我合并()Author对象和合并()的书对象。 但由于藏书是作者的组件对象,这是试图挽救它的两倍。 我的解决办法是改变级联类型:

  @OneToMany(mappedBy = "authorID", cascade =CascadeType.PERSIST, fetch=FetchType.EAGER)
  private Collection bookCollection;

 @ManyToOne(cascade =CascadeType.MERGE)
 private AuthorID authorID;

为了使长话短说,坚持父对象和合并的子对象。

希望这有助于/有意义。



文章来源: IllegalStateException with Hibernate 4 and ManyToOne cascading