Found shared references to a collection org.hibernate.HibernateException
Asked Answered
P

17

87

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 ?

Pouter answered 7/11, 2009 at 12:27 Comment(2)
I ran into the same exception, for me it was caused by having the same attribute twice in the inheritance path of my entity classMalonylurea
I ran into the same problem as @Malonylurea (i.e. the same property more than once in inheritance path), but I got the exception only when the same criteria was run twice (calling list() twice). Tricky.Harmonious
F
95

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?

Ferdy answered 7/11, 2009 at 20:4 Comment(3)
found the problem. I did a mistake by typing person.relatedPerson = anotherPerson ; somewhere in the code .... doh.Pouter
yes, but why hibernate treats this as a problem? An entity has a collection like a ManyToMany field, and the value is set by looking up from another entity.Shearwater
Look for that type of issues vary carefully, my case was quite hard to find. In my code I had few entities, of types A, B, C, D. There are relations between them A oneToOne B B oneToMany C - collection of Cs is fetched lazily there C oneToMany D - collection of Ds is fetched eagerly there In code there was a call using collection of Cs. Because of that call, collection of Cs was fetched, and some of them had same references to collections of Ds. Then instance of A was saved, which also saved B, with collection of Cs (and in some cases Cs had reference to same Ds collection).Milldam
A
78

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

Accompaniment answered 1/11, 2011 at 20:22 Comment(6)
what class should implement clone() ?Rosenzweig
@vacuum, the domain model.Dolichocephalic
what would be the solution here?Dowdell
@Dowdell Earlier, I was doing entity1.setCollection(entity2.getCollection()). I solved it by entity1.setCollection(new HashSet(entity2.getCollection()))Wolverhampton
Thanks for the checklist. In my case I was doing BeanUtils.copyProperties(oldRec, newRec) in 'clone()` method. I updated the BeanUtils.copyProperties to skip the newly added ManyToMany field as BeanUtils.copyProperties(oldRec, newRec, newCollectionField).Hoitytoity
@Wolverhampton - Got helped from your comment. ThanksScarab
S
7

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);
Savona answered 7/9, 2012 at 9:11 Comment(0)
B
7

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

Bookman answered 10/8, 2016 at 15:56 Comment(0)
D
6

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.

Denitrify answered 30/7, 2020 at 16:50 Comment(1)
You saved my day. I had the same issue, but because of non-unique composite key for many-to-many relation. Thanks a lot! Where can I read about this Hibernate feature?Tideland
G
3

In my case, I was copying and pasting code from my other classes, so I did not notice that the getter code was bad written:

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

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

All references conceptoses but if you look at the get says letrases

Guidon answered 23/6, 2016 at 21:21 Comment(1)
I am sorry, but this is not really helpful as it's just a simple mistake on your side.Gombroon
M
3

I too got the same issue, someone used BeanUtils.copyProperties(source, target). Here both source and target, are using the same collection as attribute.

So i just used the deep copy as below..

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

Mcavoy answered 25/7, 2017 at 12:28 Comment(0)
P
2

Consider an entity:

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

And consider an Business Logic class:

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

Here the user and foo.getUser() share the same reference. But saving the two references creates a conflict.

The proper usage should be:

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

This avoids the conflict.

Photozincography answered 7/8, 2017 at 9:32 Comment(1)
This is unnecessarily hard to read because class names are lower case and the code contains unnecessary assignments.Homograft
T
1

My problem was that I had setup an @ManyToOne relationship. Maybe if the answers above don't fix your problem you might want to check the relationship that was mentioned in the error message.

Taryn answered 20/5, 2020 at 8:49 Comment(0)
C
1

I was getting same error. And in my case, I had an incorrect relationship between my parent and child entities. I had @OneToMany, and changing it to @ManyToMany solved my problem.

Cody answered 10/9, 2023 at 12:31 Comment(0)
S
0

I faced similar exception in my application. After looking into the stacktrace it was clear that exception was thrown within a FlushEntityEventListener class.

In Hibernate 4.3.7 the MSLocalSessionFactory bean no longer supports the eventListeners property. Hence, one has to explicitly fetch the service registry from individual Hibernate session beans and then set the required custom event listeners.

In the process of adding custom event listeners we need to make sure the corresponding default event listeners are removed from the respective Hibernate session.

If the default event listener is not removed then the case arises of two event listeners registered against same event. In this case while iterating over these listeners, against first listeners any collections in the session will be flagged as reached and while processing the same collection against second listener would throw this Hibernate exception.

So, make sure that when registering custom listeners corresponding default listeners are removed from registry.

Supersensual answered 18/1, 2018 at 17:59 Comment(0)
L
0

Posting here because it's taken me over 2 weeks to get to the bottom of this, and I still haven't fully resolved it.

There is a chance, that you're also just running into this bug which has been around since 2017 and hasn't been addressed.

I honestly have no clue how to get around this bug. I'm posting here for my sanity and hopefully to shave a couple weeks of your googling. I'd love any input anyone may have, but my particular "answer" to this problem was not listed in any of the above answers.

Landa answered 19/8, 2020 at 20:16 Comment(0)
A
0

I had to replace the following collection initilization:

challenge.setGoals(memberChallenge.getGoals());

with

    challenge.setGoals(memberChallenge.getGoals()
                                      .stream()
                                      .map(dmo -> {
                                          final ChallengeGoal goal = new ChallengeGoalImpl();
                                          goal.setMemberChallenge(challenge);
                                          goal.setGoalDate(dmo.getGoalDate());
                                          goal.setGoalValue(dmo.getGoalValue());

                                          return goal;
                                      })
                                      .collect(Collectors.toList()));
Aristocracy answered 24/3, 2022 at 12:25 Comment(0)
C
0

I changed

@OneToMany( cascade= CascadeType.ALL)
    
@JoinColumn(
            name = "some_id",
            referencedColumnName = "some_id"
    )

to

@OneToMany(mappedBy = "some_id", cascade= CascadeType.ALL)
Croesus answered 26/9, 2022 at 16:43 Comment(0)
F
0

You're using pointers(indirectly), so sometimes you're copying the memory address instead of the object/collection you want. Hibernate checks this and throw that error. Here's what can you do:

  1. Don't copy the object/collection;
  2. Initiate a new empty one;
  3. Make a function to copy it's content and call it;

For example:

public Entity copyEntity(Entity e){
    Entity copy = new Entity();
    e.copy(name);
    e.setCollection2(null);
    e.setCollection3(copyCollection(e.getCollection3());
    return copy;
}
Fabre answered 10/1, 2023 at 15:52 Comment(0)
T
0

It basically means that JPA doesn't want to save the exact same instance again, just being associated (set) to another object. Create a deep copy instead, and you should be fine.

Thirtytwomo answered 23/10, 2023 at 4:57 Comment(0)
W
-1

In a one to many and many to one relationship this error will occur. If you attempt to devote same instance from many to one entity to more than one instance from one to many entity.

For example, each person can have many books but each of these books can be owned by only one person if you consider more than one owner for a book this issue is raised.

Whistle answered 14/7, 2020 at 12:23 Comment(1)
Could you please edit to make this a little more readable?Gameto

© 2022 - 2024 — McMap. All rights reserved.