当前位置: 首页 > 面试题库 >

GSON +休眠:导致“一个实体副本已分配给另一个实体”的相同对象

公西博实
2023-03-14
问题内容

我有以下JSON:

{
    id: 123,
    subObjects: [
        {
            id: 564,
            name: "foo",
            contry: {
                id: 1,
                name: "Germany"
            }
        },
        {
            id: 777,
            name: "bar",
            contry: {
                id: 1,
                name: "Germany"
            }
        }
    ]   
}

并使用Gson反序列化它。之后,我需要合并JPA实体:

Model model = new Gson().fromJson(json, modelClass);
model = entityManager.merge(model)

刷新从模型级联到子对象,再级联到各个国家。这会导致Hibernate出现异常“已将实体副本分配给其他实体”。

如果我使用其他国家/地区,则可以使用。如果我使用将国家/地区实例从一个对象复制到另一个对象,那么这两个子对象都引用该对象的同一实例,则该方法有效。

两国具有相同的价值观。两者也具有相同的hashCode。两国是平等的,但不是==,因为它们是不同的情况。

我正在使用Hibernate 4.1.3 Final和Gson 2.2

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.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:439)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:308)
    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.cascadeCollectionElements(Cascade.java:409)
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
    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.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:439)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:308)
    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 play.db.jpa.GenericModel.merge(GenericModel.java:234)
    [...]

我该如何以通用的方式解决此问题,而无需知道对象类型以及哪些对象在逻辑上是相同的?


问题答案:

这是我不使用合并的工作解决方案:

我使用反射在绑定时遍历字段。我检查该字段是实体还是实体集合,如果json中提供了ID,则调用findByID;如果不获取附加实体,则创建该实体的新实例。之后,我通过反射将json中存在的字段值复制到附加的实体中。我递归地处理实体之间的关系。之后,我将拥有一个完整的合并和附加版本的实体以及所有附加和合并的子对象。我可以毫无问题地保存它

重要的提示:

安德烈(Andrei)我提出了许多可能发生的逻辑问题,并且更改了api使其无法获得完整的国家/地区,但仅凭其ID会更清楚。我喜欢这个,将来会做。因此,安德烈·我赢得了赏金。



 类似资料:
  • 问题内容: 我有一个实体- 。由描述。 Hibernate为每个实体创建一个表,因此当我调用时,我的数据总是保存到该表中。 现在,我需要另一个表来存储相同类型的数据,并且只需要将我的实体保存到该表中。 数据结构 (类似这样): 使用此方法 : 和结果,我应该在和 中。 由于系统限制,我无法将这两个对象放在一个表中。(即使创建额外的字段也是个坏主意)。 我可以不进行子类化吗?使用Programmat

  • 问题内容: 我使用Hibernate 4和Spring 3。 我有两个实体。 图书实体 和作者实体 和JSON取决于pom.xml 我的根上下文在这里- … servlet-context.xml 控制器。 在我的DAO中找到findAll: 在调试中,我看到该方法返回2条记录,但是Spring无法将结果转换为JSON并返回406 HTTP错误。怎么了? 我附上我在调试中看到的图像。- http:

  • 问题内容: 我想要3个Java类的对象之间的 单向 一对一关系:“人到心脏”和“人到肝脏”。我希望对象共享相同的PK,即每个人都有相应的心脏和肝脏,其中person.person_id = heart.heart_id = liver.liver_id。我不想将3个表合并为1个表,因为每个表都有很多字段。这是我的代码(主要基于该问题的公认答案): 我设置了会话并执行以下操作: 然后,如果我将人对象

  • 我在代码中偶然发现了一个奇怪的bug。以前是有效的,但现在有时也有效。 我正在使用EF6编辑具有某些关系的实体。为了不编辑关系,我“附加”了它们(参见示例代码)。 我编辑了这些名字以保持简单。 以下行 抛出此错误: 附加类型为“C”的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Mo

  • 让我快速描述一下我的问题。 我有5个客户的5个数据库,每个数据库都有一个名为SubnetSettings的表。 我已经创建了一个下拉列表来选择一个客户,并将显示属于所选客户的SubnetSSet表,并允许我创建、编辑和删除。 我可以毫无问题地创建、删除,但当我想编辑数据时,它会带来错误: /运输管理系统应用程序中的服务器错误。 附加“CFS”类型的实体。领域实体。SubnetSettings“失败

  • 错误消息:附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 问题:我如何以类似于下面代码中AttachActivity方法所示的方式附加实体? 我必须假设上面错误消息的“另一个实体”部分指的是内存中存在但超出范围的对象(?)。我之所以注意到这一点,是因为我尝试附加的实体类型的DBSet的本地属性返回零。 我有理由相信实体不存在于上下文中,因为我遍历代码并在创建上下文时观察上下文。实