Found shared references to a collection org.hibernate.HibernateException

I got this error message:

error: Found shared references to a collection: Person.relatedPersons

When I tried to execute addToRelatedPersons(anotherPerson):

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

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

My domain:

Person {

 static hasMany = [relatedPersons:Person];

}

any idea why this happens ?


Solution 1:

Hibernate shows this error when you attempt to persist more than one entity instance sharing the same collection reference (i.e. the collection identity in contrast with collection equality).

Note that it means the same collection, not collection element - in other words relatedPersons on both person and anotherPerson must be the same. Perhaps you're resetting that collection after entities are loaded? Or you've initialized both references with the same collection instance?

Solution 2:

I had the same problem. In my case, the issue was that someone used BeanUtils to copy the properties of one entity to another, so we ended up having two entities referencing the same collection.

Given that I spent some time investigating this issue, I would recommend the following checklist:

  • Look for scenarios like entity1.setCollection(entity2.getCollection()) and getCollection returns the internal reference to the collection (if getCollection() returns a new instance of the collection, then you don't need to worry).

  • Look if clone() has been implemented correctly.

  • Look for BeanUtils.copyProperties(entity1, entity2).

Solution 3:

Explanation on practice. If you try to save your object, e.g.:

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

you don't need to set updated object to a parent object:

message.setFiles(folders);

Simple save your parent object like:

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

Solution 4:

Reading online the cause of this error can be also an hibernate bug, as workaround that it seems to work, it is to put a:

session.clear()

You must to put the clear after getting data and before commit and close, see example:

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

I use this solution for select to database, for update or insert i don't know if this solution can work or can cause problems.

My problem is equal at 100% of this: http://www.progtown.com/topic128073-hibernate-many-to-many-on-two-tables.html

Solution 5:

I have experienced a great example of reproducing such a problem. Maybe my experience will help someone one day.

Short version

Check that your @Embedded Id of container has no possible collisions.

Long version

When Hibernate instantiates collection wrapper, it searches for already instantiated collection by CollectionKey in internal Map.

For Entity with @Embedded id, CollectionKey wraps EmbeddedComponentType and uses @Embedded Id properties for equality checks and hashCode calculation.

So if you have two entities with equal @Embedded Ids, Hibernate will instantiate and put new collection by the first key and will find same collection for the second key. So two entities with same @Embedded Id will be populated with same collection.

Example

Suppose you have Account entity which has lazy set of loans. And Account has @Embedded Id consists of several parts(columns).

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

    ...
}

Then suppose that Account has additional property mapped by @Embedded Id but has relation to other entity Branch.

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

It could happen that you have no FK for Account to Brunch relation id DB so Account.BRANCH column can have any value not presented in Branch table.

According to @NotFound(action = NotFoundAction.IGNORE) if value is not present in related table, Hibernate will load null value for the property.

If X and Y columns of two Accounts are same(which is fine), but BRANCH is different and not presented in Branch table, hibernate will load null for both and Embedded Ids will be equal.

So two CollectionKey objects will be equal and will have same hashCode for different Accounts.

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"

Because of this, Hibernate will load same PesistentSet for two entities.