找到了对集合org.hibernate.HibernateException的共享引用

时间:2009-11-07 12:27:26

标签: java hibernate grails groovy gorm

我收到此错误消息:

  

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

当我尝试执行addToRelatedPersons(anotherPerson)时:

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

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

我的域名:

Person {

 static hasMany = [relatedPersons:Person];

}

知道为什么会这样吗?

12 个答案:

答案 0 :(得分:57)

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

请注意,它表示相同的集合,而不是集合元素 - 换言之,relatedPersonsperson上的anotherPerson必须相同。也许你在加载实体后重置那个集合?或者您已使用相同的集合实例初始化两个引用?

答案 1 :(得分:47)

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

鉴于我花了一些时间研究这个问题,我建议如下清单:

  • 查找entity1.setCollection(entity2.getCollection())getCollection等方案返回集合的内部引用(如果getCollection()返回集合的新实例,那么您不必担心)

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

  • 寻找BeanUtils.copyProperties(entity1, entity2)

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

在线阅读此错误的原因也可能是 hibernate错误,因为解决方法它似乎有用,它是放一个:

session.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

答案 4 :(得分:2)

在我的情况下,我正在复制并粘贴其他类中的代码,因此我没有注意到getter代码编写错误:

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

public void setConceptoses(Set conceptoses) {
    this.conceptoses = conceptoses;
}

所有参考文献 概念 ,但如果你看看获取 letrases

答案 5 :(得分:2)

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

简短版

检查您的@Embedded ID容器是否没有冲突。

长版

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

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

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

示例

假设您有一个帐户实体,其中包含一组懒惰的贷款。 帐户的@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;

    ...
}

然后假设该帐户具有@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将为属性加载值。

如果两个帐户的X和Y列相同(很好),但BRANCH不同且未在Branch表中显示,则hibernate会加载 null ,并且Embedded Ids会相等。

因此,两个CollectionKey对象将相等,并且对于不同的帐户将具有相同的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。

答案 6 :(得分:1)

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

所以我只是使用下面的深层复制..

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

答案 7 :(得分:0)

我的申请中遇到了类似的例外情况。在查看堆栈跟踪之后,很明显异常是在FlushEntityEventListener类中抛出的。

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

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

如果未删除默认事件侦听器,则会出现两个针对同一事件注册的事件侦听器的情况。在这种情况下,在迭代这些侦听器时,对于第一个侦听器,会话中的任何集合都将被标记为已达到,并且在针对第二个侦听器处理相同集合时将抛出此Hibernate异常。

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

在这里发布是因为我花了2个多星期才了解到这一点,而我仍然仍未完全解决它。

有一个机会,您也刚遇到this bug which has been around since 2017 and hasn't been addressed

老实说,我不知道如何解决此错误。我在这里发帖是出于理智,希望刮胡子几个星期。我希望任何人都可以提供任何输入,但是上述答案的 any 中没有列出我对这个问题的特定“答案”。

答案 10 :(得分:-1)

考虑一个实体:

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

这可以避免冲突。

答案 11 :(得分:-1)

一对多 多对一 关系中,将发生此错误。如果您尝试将同一实例从 多到一个 实体分配到一个以上实例,从 一到多 实体

例如,每个人可以拥有多本书,但是如果您考虑到一本书的所有者不止一个,那么每个书只能由一个人拥有。