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


68

我收到此错误消息:

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

当我尝试执行时addToRelatedPersons(anotherPerson)

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

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

我的网域:

Person {

 static hasMany = [relatedPersons:Person];

}

知道为什么会这样吗?


1
我遇到了同样的异常,对我来说,这是由我的实体类的继承路径中两次具有相同的属性引起的
kaefert

我遇到了与@kaefert相同的问题(即,相同的属性在继承路径中不止一次),但只有在相同的条件运行两次(调用list()两次)时,我才出现异常。整rick
VlastimilOvčáčík16年

Answers:


74

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

请注意,这意味着同样的集合,而不是集合元素-换句话说,relatedPersons在两个personanotherPerson必须相同。也许您是在实体加载后重置该集合?或者您已经使用相同的集合实例初始化了两个引用?


4
发现了问题。我通过输入person.relatedPerson = anotherPerson犯了一个错误;代码中的某处.. doh。
nightingale2k1,2009年

7
是的,但是为什么冬眠将此视为问题?一个实体具有一个类似于ManyToMany字段的集合,并且该值是通过从另一个实体中查找来设置的。
蒂娜

55

我有同样的问题。在我的情况下,问题是有人使用BeanUtils将一个实体的属性复制到另一个实体,因此我们最终得到两个引用同一集合的实体。

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

  • 查找类似的场景,entity1.setCollection(entity2.getCollection())getCollection返回对集合的内部引用(如果getCollection()返回集合的新实例,则无需担心)。

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

  • 寻找BeanUtils.copyProperties(entity1, entity2)


什么类应该实现clone()?
真空

@vacuum,域模型。
鲁本斯·马里努佐

这里的解决方案是什么?
vashishth

1
@vashishth之前,我在做entity1.setCollection(entity2.getCollection())。我通过entity1.setCollection(new HashSet(entity2.getCollection()))
Bhushan

感谢您的清单。就我而言,我正在BeanUtils.copyProperties(oldRec, newRec)使用'clone()`方法。我更新了,BeanUtils.copyProperties以跳过新添加的ManyToMany字段BeanUtils.copyProperties(oldRec, newRec, newCollectionField)
gtiwari333 '18

6

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

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

5

在线阅读引起该错误的原因也可能是休眠错误,因为似乎可以解决该问题的方法是,将以下内容放入:

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


3

就我而言,我正在从其他类中复制和粘贴代码,因此我没有注意到getter代码编写不正确:

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

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

所有参考文献都是概念性的,但是如果您看到“ getrases”


2
很抱歉,但这实际上并没有帮助,因为这只是您一方的一个简单错误。
ichalos


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映射的其他属性,但与其他实体“分支”有关系。

@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表中显示,则休眠将为这两个加载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。


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

这避免了冲突。


1
由于类名是小写字母,并且代码包含不必要的分配,因此这很难理解。
卡尔·里希特

0

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

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

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

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

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


0

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


0

在这里发帖是因为我花了2个多星期才能深入了解这一点,但我仍然没有完全解决它。

可能您还遇到了自2017年以来一直存在且尚未解决的bug

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


-1

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

例如,每个人可以拥有多本书,但是如果您考虑到一本书的所有者超过一个,则会引发此问题,但是每本书只能由一个人拥有。


您能编辑一下使它更具可读性吗?
菲尔多
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.