【问题标题】:Found shared references to a collection org.hibernate.HibernateException找到对集合 org.hibernate.HibernateException 的共享引用
【发布时间】:2010-12-14 03:40:50
【问题描述】:

我收到此错误消息:

错误:找到对集合的共享引用:Person.relatedPersons

当我试图执行addToRelatedPersons(anotherPerson):

person.addToRelatedPersons(anotherPerson);
anotherPerson.addToRelatedPersons(person);

anotherPerson.save();
person.save();

我的域名:

Person {

 static hasMany = [relatedPersons:Person];

}

知道为什么会这样吗?

【问题讨论】:

  • 我遇到了同样的异常,对我来说这是由于我的实体类的继承路径中有两次相同的属性引起的
  • 我遇到了与@kaefert 相同的问题(即在继承路径中多次使用相同的属性),但只有在两次运行相同的条件时才出现异常(两次调用list()) .棘手。

标签: java hibernate grails groovy grails-orm


【解决方案1】:

当您尝试持久化多个共享相同集合引用(即集合标识与集合相等性对比)的实体实例时,Hibernate 会显示此错误。

请注意,这意味着相同的collection,而不是collection 元素——换句话说,person 和anotherPerson 上的relatedPersons 必须相同。也许您正在加载实体后重置该集合?或者你已经用同一个集合实例初始化了两个引用?

【讨论】:

  • 发现了问题。我输入 person.relatedPerson = anotherPerson 犯了一个错误;代码中的某处.... doh.
  • 是的,但为什么 hibernate 将其视为一个问题?一个实体有一个类似ManyToMany字段的集合,并且值是通过从另一个实体查找来设置的。
【解决方案2】:

我遇到了同样的问题。就我而言,问题在于有人使用 BeanUtils 将一个实体的属性复制到另一个实体,因此我们最终得到了两个实体引用同一个集合。

鉴于我花了一些时间调查此问题,我建议使用以下清单:

  • 寻找像entity1.setCollection(entity2.getCollection())和getCollection这样的场景返回集合的内部引用(如果getCollection()返回一个新的集合实例,那么你不用担心)。

  • 查看clone()是否已正确实现。

  • 寻找BeanUtils.copyProperties(entity1, entity2)。

【讨论】:

  • 什么类应该实现clone()?
  • @vacuum,领域模型。
  • 这里的解决方案是什么?
  • @vashishth 早些时候,我在做entity1.setCollection(entity2.getCollection())。我通过entity1.setCollection(new HashSet(entity2.getCollection()))解决了它
  • 感谢您的清单。就我而言,我在 'clone()` 方法中执行 BeanUtils.copyProperties(oldRec, newRec)。我更新了BeanUtils.copyProperties 以跳过新添加的ManyToMany 字段为BeanUtils.copyProperties(oldRec, newRec, newCollectionField)。
【解决方案3】:

练习说明。如果您尝试保存对象,例如:

Set<Folder> folders = message.getFolders();
   folders.remove(inputFolder);
   folders.add(trashFolder);
   message.setFiles(folders);
MESSAGESDAO.getMessageDAO().save(message);

您不需要将更新的对象设置为父对象:

message.setFiles(folders);

简单地保存你的父对象:

Set<Folder> folders = message.getFolders();
   folders.remove(inputFolder);
   folders.add(trashFolder);
   // Not set updated object here
MESSAGESDAO.getMessageDAO().save(message);

【讨论】:

    【解决方案4】:

    网上看了下这个错误的原因也可能是hibernate bug,作为workaround看来是可行的,就是放一个:

    session.clear()
    

    你必须在获取数据之后,在提交和关闭之前把clear,看例子:

    //getting data
    SrReq sr = (SrReq) crit.uniqueResult();
    SrSalesDetailDTO dt=SrSalesDetailMapper.INSTANCE.map(sr);
    //CLEAR            
    session.clear();
    //close session
    session.getTransaction().commit();
    session.close();
    return dt;
    

    我使用此解决方案选择数据库,更新或插入我不知道此解决方案是否可行或会导致问题。

    我的问题等于 100%:http://www.progtown.com/topic128073-hibernate-many-to-many-on-two-tables.html

    【讨论】:

      【解决方案5】:

      我经历过重现此类问题的一个很好的例子。 也许有一天我的经验会对某人有所帮助。

      短版

      检查您的容器的@Embedded Id 是否没有可能的冲突。

      加长版

      当 Hibernate 实例化集合包装器时,它会在内部 Map 中通过 CollectionKey 搜索已经实例化的集合。

      对于具有@Embedded id 的实体,CollectionKey 包装 EmbeddedComponentType 并使用 @Embedded Id 属性进行相等性检查和 hashCode 计算。

      因此,如果您有两个具有相同 @Embedded Id 的实体,Hibernate 将通过第一个键实例化并放置新集合,并为第二个键找到相同的集合。 因此,具有相同 @Embedded Id 的两个实体将填充相同的集合。

      示例

      假设您的 Account 实体具有惰性贷款集。 并且 Account 有 @Embedded Id 由几个部分(列)组成。

      @Entity
      @Table(schema = "SOME", name = "ACCOUNT")
      public class Account {
          @OneToMany(fetch = FetchType.LAZY, mappedBy = "account")
          private Set<Loan> loans;
      
          @Embedded
          private AccountId accountId;
      
          ...
      }
      
      @Embeddable
      public class AccountId {
          @Column(name = "X")
          private Long x;
          
          @Column(name = "BRANCH")
          private String branchId;
          
          @Column(name = "Z")
          private String z;
      
          ...
      }
      

      然后假设 Account 具有由 @Embedded Id 映射的附加属性,但与其他实体 Branch 有关系。

      @ManyToOne(fetch = FetchType.EAGER)
      @JoinColumn(name = "BRANCH")
      @MapsId("accountId.branchId")
      @NotFound(action = NotFoundAction.IGNORE)//Look at this!
      private Branch branch;
      

      您可能没有帐户到早午餐关系 id DB 的 FK,因此 Account.BRANCH 列可以具有分支表中未显示的任何值。

      根据@NotFound(action = NotFoundAction.IGNORE),如果相关表中不存在值,Hibernate 将为该属性加载 null 值。

      如果两个 Account 的 X 和 Y 列相同(这很好),但 BRANCH 不同且未显示在 Branch 表中,则 hibernate 将为两者加载 null 并且嵌入的 Id 将相等。

      因此,两个 CollectionKey 对象将相等,并且对于不同的 Accounts 具有相同的 hashCode。

      result = {CollectionKey@34809} "CollectionKey[Account.loans#Account@43deab74]"
       role = "Account.loans"
       key = {Account@26451} 
       keyType = {EmbeddedComponentType@21355} 
       factory = {SessionFactoryImpl@21356} 
       hashCode = 1187125168
       entityMode = {EntityMode@17415} "pojo"
      
      result = {CollectionKey@35653} "CollectionKey[Account.loans#Account@33470aa]"
       role = "Account.loans"
       key = {Account@35225} 
       keyType = {EmbeddedComponentType@21355} 
       factory = {SessionFactoryImpl@21356} 
       hashCode = 1187125168
       entityMode = {EntityMode@17415} "pojo"
      

      因此,Hibernate 将为两个实体加载相同的 PesistentSet。

      【讨论】:

      • 你拯救了我的一天。我有同样的问题,但由于多对多关系的非唯一复合键。非常感谢!我在哪里可以了解这个 Hibernate 功能?
      【解决方案6】:

      就我而言,我是从其他类中复制和粘贴代码,所以我没有注意到 getter 代码写得不好:

      @OneToMany(fetch = FetchType.LAZY, mappedBy = "credito")
      public Set getConceptoses() {
          return this.letrases;
      }
      
      public void setConceptoses(Set conceptoses) {
          this.conceptoses = conceptoses;
      }
      

      所有参考 conceptoses 但如果你看得到说 letrases

      【讨论】:

      • 很抱歉,但这并没有真正的帮助,因为这只是您的一个简单错误。
      【解决方案7】:

      我也遇到了同样的问题,有人用BeanUtils.copyProperties(source, target)。这里源和目标都使用相同的集合作为属性。

      所以我只是使用下面的深拷贝..

      How to Clone Collection in Java - Deep copy of ArrayList and HashSet

      【讨论】:

        【解决方案8】:

        考虑一个实体:

        public class Foo{
        private<user> user;
        /* with getters and setters */
        }
        

        并考虑一个业务逻辑类:

        class Foo1{
        List<User> user = new ArrayList<>();
        user = foo.getUser();
        }
        

        这里用户和foo.getUser() 共享相同的引用。但是保存这两个引用会产生冲突。

        正确的用法应该是:

        class Foo1 {
        List<User> user = new ArrayList<>();
        user.addAll(foo.getUser);
        }
        

        这样可以避免冲突。

        【讨论】:

        • 这太难读了,因为类名是小写的,而且代码中包含不必要的赋值。
        【解决方案9】:

        我在我的应用程序中遇到了类似的异常。查看堆栈跟踪后,很明显在 FlushEntityEventListener 类中引发了异常。

        在 Hibernate 4.3.7 中,MSLocalSessionFactory bean 不再支持 eventListeners 属性。因此,必须从单个 Hibernate 会话 bean 中显式获取服务注册表,然后设置所需的自定义事件侦听器。

        在添加自定义事件监听器的过程中,我们需要确保从各自的 Hibernate 会话中移除相应的默认事件监听器。

        如果默认事件监听器没有被移除,那么就会出现两个事件监听器针对同一个事件注册的情况。在这种情况下,在遍历这些侦听器时,针对第一个侦听器,会话中的任何集合都将被标记为已到达,并且在针对第二个侦听器处理相同的集合时,将抛出此 Hibernate 异常。

        因此,请确保在注册自定义侦听器时,从注册表中删除相应的默认侦听器。

        【讨论】:

          【解决方案10】:

          我的问题是我设置了@ManyToOne 关系。也许如果上面的答案不能解决您的问题,您可能需要检查错误消息中提到的关系。

          【讨论】:

            【解决方案11】:

            在这里发帖是因为我花了 2 多周的时间才弄清这个问题,而我仍然还没有完全解决它。

            有一个机会,你也只是遇到this bug which has been around since 2017 and hasn't been addressed。

            老实说,我不知道如何解决这个错误。我在这里发帖是为了我的理智,希望能减少你在谷歌上搜索几周的时间。我希望任何人都可以提供任何意见,但我对这个问题的特定“答案”未列在上述任何答案中。

            【讨论】:

              【解决方案12】:

              在一对多和多对一关系中会发生此错误。如果您尝试将 ma​​ny to one 实体的相同实例用于 one to many 实体的多个实例.

              例如,每个人可以拥有多本书,但如果您认为一本书有多个所有者,则每本书只能归一个人所有。

              【讨论】:

              • 能否请您进行编辑以使其更具可读性?
              猜你喜欢
              • 2011-08-07
              • 2020-07-19
              • 2014-11-26
              • 2012-08-12
              • 1970-01-01
              • 2012-10-14
              • 1970-01-01
              • 1970-01-01
              • 2019-10-24
              相关资源
              最近更新 更多