If I save an object containing the following list
#OneToMany(cascade=CascadeType.ALL, mappedBy="taskList")
#OrderColumn(name="position", nullable=false)
public List<Task> tasks = new ArrayList<Task>();
I get the exception
org.hibernate.HibernateException: Found two representations of same collection
The code in the Play! controller looks like this:
TaskList taskList = taskList.findById(taskListId);
taskList.add(position, task);
taskList.save();
If I insert taskList.refresh() before this block it works, but the position information is lost (which leads to other errors).
Is this a Hibernate bug or is something wrong with my code?
The problem was, that Hibernate does not support the combination of #OneToMany(mappedBy=...) and #OrderColumn. Without mappedBy Hibernate uses a join table and everything works as expected. See explanation.
The same error occurs when you try to modify an associated collection of an object. e.g.:
MyObject myObject = myObjectService.get(id);
List<Task> newTasks = //populate new list of Task here
myObject.setTasks(newTasks);
myObjectService.saveOrUpdateObject(myObject); // or merge(myObject)
In such a case, it can be resolved by:
MyObject myObject = myObjectService.get(id);
List<Task> newTasks = //populate new list of Task here
myObject.setTasks(new List<Task>); // or myObject.getTasks().clear();
myObject.getTasks().addAll(newTasks);
myObjectService.merge(myObject);
I could resolve the issue by changing the association to lazy and removing the cascade.
#OneToMany(mappedBy="taskList", fetch = FetchType.LAZY)
#OrderColumn(name="position", nullable=false)
public List<Task> tasks = new ArrayList<Task>();
If you need the list to be persisted you might need to annotate your list with #OneToMany to be able to persist your entity.
Another reason for the exception "org.hibernate.HibernateException: Found two representations of same collection" is duplicated getters/setters in the entity.
After a lot of hours I found the solution, I had the same problem.
I had an EntityListener with this code in my Spring Boot app
#PostLoad
public void defineRelation(EntityWithRelation entityWithRelation) {
entityWithRelation.setRelation(objectRelationBo
.findOneByObjectTypeAndReferenceId(entityWithRelation.getObject(), entityWithRelation.getId()));
}
This was causing the exception in a random (or looks like) collection of other unrelated entity that was participating in the same transaction. I don't know the exact reason but adding #Transactional(propagation = Propagation.REQUIRES_NEW) fixes the problem
Maybe Hibernate doesn't like you to modify the entity by hand, but the field was a transient field...
the problem is with your joining check whether you are not fatching any data which is not in the table
Related
I made my self a little hibernate sandbox to understand how it works.
I ve done quite well so far with all the basics. Everything works as expected.
I only have an unsolved issue.
To make it short, I have a Rats entity and a Sickness entity.
A Rat can have a single Sickness.
The association is correctly set into the DB and the entities files include this part:
in Rats class:
[...]
#ManyToOne(fetch = FetchType.LAZY )
#Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#JoinColumn(name = "Sickness_Id")
public Sickness getSickness() {
return this.sickness;
}
[...]
in Sickness class:
[...]
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sickness")
#Cascade({ /*CascadeType.SAVE_UPDATE,*/ CascadeType.MERGE, CascadeType.REFRESH})
public Set<Rats> getRatses() {
return this.ratses;
}
[...]
If I create a new Rats with a new Sickness and save the Rats, the cascade works as expected and the Sickness is automatically added to the DB too.
Deletion part works too, when I delete a Rats, its sickness is deleted.
What does not work is trying to create a Sickness and try to spread it to many Rats via its SetRatses method:
String sick_name2 = "Tourista";
System.out.println("\nsetting new sickness: " + sick_name2 + " and assigning it to all rats");
Sickness sickness2 = new Sickness();
sickness2.setNom(sick_name2);
ArrayList<Rats> sickratsList = (ArrayList<Rats>) session.createCriteria(Rats.class).list();// new HashSet<Rats>();
Set<Rats> sickRatsSet = new HashSet<Rats>();
for(Rats rat : sickratsList){
sickRatsSet.add(rat);
}
sickness2.setRatses(sickRatsSet);
session.save(sickness2);
Debuging this shows that the Sickness is correctly inserted into the DB, its sickRatsSet field is correctly set with all the rats.
But... if I check the Rats status, their Sickness has not been updated.
Trying to set CascadeType. into the Sickness relationship did not help.
I know that I could solve it with something like:
ArrayList<Rats> sickratsList = (ArrayList<Rats>) session.createCriteria(Rats.class).list();// new HashSet<Rats>();
Set<Rats> sickRatsSet = new HashSet<Rats>();
for(Rats rat : sickratsList){
rat.setSickness(seckness2);
session.save(rat);
}
But I would like to understand how to do it via Sickness.setRatses.
So that I can find my way later with a many to many relationship (I suppose it will be pretty similar).
Thx in advance.
A bidirectional association has an owner side (the side without the mappedBy attribute), and an inverse side (the side with the mappedBy attribute).
Hibernate only considers the owner side when deciding which entities are associated with each other.
Adding rats to a sickness thus won't make Hibernate associate the rat to the sickness, since that only modifies the inverse side. You must set the rat's sickness.
Note that using a DELETE cascade on a ManyToXxx annotation doesn't make much sense. There is no reason to delete the tourista sickness from the database as soon as one of the thousand rats having the tourista is deleted. And that will obviously cause an exception anyway, since 999 other rats have a foreign key to the tourista sickness.
This error happens because you're retrieving the objects that are already cached in the 1st level cache (session), and those objects don't have the bidirectional association set correctly. In your code, you're never calling rat.setSickeness(sickness).
Try calling the following methods and check if the data is now correct
session.flush()
session.clear()
// load the rats /sickness again and the relations should be set.
Bottom line: when you have a bidirectional association is the developer responsibility to add/set the objects on boths ends, otherwise you'll get into this error. The simplest way to fix this is to only have one method in one of your 2 objects that know how to maintain the assocation. For example
public class Rat {
public void setSickness(Sickness sickness) {
this.sicknesses = sickness;
sickness.addRat(this);
}
}
public class Sickness {
// leave this as package protected! So the only way to set the association is from the Rat
void addRat(Rat rat) {
rats.add(rat);
}
}
You might want to read the Hibernate documentation about Session and how it works as a 1st level cache.
Thanks JB and Augusto, I got a much better understanding now.
I was able to solve my issue by overriding this way:
public void setRatses(Set<Rats> ratses) {
this.ratses = ratses;
for(Rats rat : ratses){
rat.setSickness(this);
}
This bring me another methodological question.
I found out that if I do the following:
raton.setSickness(sickness1);
raton.display(); -> raton.sickness = sickness1 as expected
sickness1.display(); -> sickness1.ratses does not contain raton for the reasons that you guys pointed.
I can either use session.flush()
session.clear() or commit the transaction and start a new one if I need sickness1.ratses to be up to date.
I suppose that I can also override Rats.setSickness this way:
public void setSickness(Sickness sickness) {
if(sickness.ratses.contain(this) sickness.ratses.remove(this);
this.sickness = sickness;
ickness.ratses.add(this);
}
this way, my sickness is up to date inside the session without I need to flush the session.
Would this be such a good idea?
On regarding performance side, I suppose that the override solution generates additional DB operations that might not be really needed?
I want to remove an item from a list in an Entity. I have this Entity :
#Entity
public class PairingCommit extends Model
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "commit")
public List<CommitItem> items;
}
I do the following for removing an Item :
commit.items.remove(item);
commit.update();
But it doesn't remove the object from the database.
I suppose i missed something up...
EDIT : After some search, I'm not sure to use JPA... I'm working with Play framework 2 that use Ebean... But it seems that I have access to JPA annotation..
My first problem was that trying to directly delete the item like this :
CommitItem.byId(id).delete();
But it give a OptimisticLockException.
You should call EntityManager 's remove method on item.
EntityManager em;
item = em.merge(item); // Now item is attached
em.find(PairingCommit.class, [Pairing Commit PK]).items.remove(item);
em.remove(item);
Take a look at this question/answer. The CascadeType annotation will propagate EntityManager operations to the linked entities. The way your code is currently set up, calling
entityManager.remove(pairingCommit);
would also delete all of the CommitItems that the PairingCommit is linked to, but
commit.items.remove(item);
is not an EntityManager operation, so nothing gets propagated.
You can get rid of the linked items directly with the EntityManager.
The specification says:
It is particularly important to ensure that changes to the inverse
side of a relationship result in appropriate updates on the owning
side, so as to ensure the changes are not lost when they are
synchronized to the database.
So, you must remove from the owning side of relation:
commitItem.setCommit(null);
Ok so I have solved the problem of Optimistick Lock. It was that mysql failed to compare floating point number. I have passed to DECIMAL type and works fine now.
But I d'ont understand why the removing of list don't works.
Here an article on how Optimistick lock works : http://www.avaje.org/occ.html
TGIF guys, but I am still stuck on one of my projects. I have two interfaces IMasterOrder and IOrder. One IMasterOrder may have a Collection of IOrder. So there can be several MasterOrder entity classes and Order entity classes who implements the interfaces.
To simplify the coding, I create IMasterOrder and IOrder objects everywhere, but when it needs to specify the concrete type then I just cast IMasterOrder object to the class type.
The problem is, this makes master class always return null about its orders. I am very curious about how JPA works with polymorphism in general?
Update
Sorry for the early confusion. Actually the question is much simpler
Actually the entity class is something like this
public class MasterOrder implements IMasterOrder {
// Relationships
#OneToOne(mappedBy = "masterOrder")
private OrderCustomFields customFields;
#OneToMany(mappedBy = "masterOrder")
private List<OrderLog> logs;
#OneToMany(mappedBy = "masterOrder")
private Collection<Order> orders;
// Fields...
And the finder method to get the Master order entity instance is like this
public static MasterOrder findMasterOrder(String id) {
if (id == null || id.length() == 0) return null;
return entityManager().find(MasterOrder.class, id);
}
However, the MasterOrder instance from this finder method returns customFields and logs and orders which are all null. So how to fix this? Thanks in advance.
When you access logs and orders, is Master still a part of an active persistence context? ie: Has the EntityManager that found the Master Entity been closed or cleared? If yes, everything is working as expected.
For giggles, you could try changing the fetch attribute on logs and orders to EAGER ... this will help pinpoint if there is something else bad going on.
#OneToMany(mappedBy = "masterOrder", fetch = FetchType.LAZY)
private List<OrderLog> logs;
#OneToMany(mappedBy = "masterOrder", fetch = FetchType.LAZY)
private Collection<Order> orders;
Sounds like a problem with your mapping.. I don't think empty collections should be NULL, they should either be an empty list (if initialized), or a proxy that will be initialized when you read from it. If you leave the transaction and try to read from the collection, it SHOULD throw a lazy initialization exception. In either case, you should include all relevant classes in the question to provide further information.
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 ?
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?
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).
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);
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
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.
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
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
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.
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.
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.
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.
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()));
I changed
#OneToMany( cascade= CascadeType.ALL)
#JoinColumn(
name = "some_id",
referencedColumnName = "some_id"
)
to
#OneToMany(mappedBy = "some_id", cascade= CascadeType.ALL)
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:
Don't copy the object/collection;
Initiate a new empty one;
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;
}
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.
I have a class called SynonymMapping which has a collection of values mapped as a CollectionOfElements
#Entity(name = "synonymmapping")
public class SynonymMapping {
#Id private String keyId;
//#CollectionOfElements(fetch = FetchType.EAGER)
#CollectionOfElements
#JoinTable(name="synonymmappingvalues", joinColumns={#JoinColumn(name="keyId")})
#Column(name="value", nullable=false)
#Sort(type=SortType.NATURAL)
private SortedSet<String> values;
public SynonymMapping() {
values = new TreeSet<String>();
}
public SynonymMapping(String key, SortedSet<String> values) {
this();
this.keyId = key;
this.values = values;
}
public String getKeyId() {
return keyId;
}
public Set<String> getValues() {
return values;
}
}
I have a test where I store two SynonymMapping objects to the database and then ask the database to return all saved SynonymMapping objects, expecting to receive the two objects I stored.
When I change the mapping of values to be eager (as shown in in the code by the commented out line) and run the test again, I receive four matches.
I have cleared out the database between runs and I can duplicate this problem swapping between eager and lazy.
I think it has to do with the joins that hibernate creates underneath but I can't find a definite answer online.
Can anyone tell me why an eager fetch is duplicating the objects?
Thanks.
I stepped into the same problem - when you set the FetchType.EAGER for a #CollectionOfElements, the Hibernate tries to get everything in one shot, i.e. using one single query for each entry of element linked to a "master" object. This problem can be successfully solved at a cost of N+1 query, if you add the #Fetch (FetchMode.SELECT) annotation to your collection.
In my case I wanted to have a MediaObject entity with a collection of its metadata items (video codec, audio codec, sizes, etc.). The mapping for a metadataItems collection looks as follows:
#CollectionOfElements (targetElement = String.class, fetch = FetchType.EAGER)
#JoinTable(name = "mo_metadata_item", joinColumns = #JoinColumn(name = "media_object_id"))
#MapKey(columns = #Column(name = "name"))
#Column (name = "value")
#Fetch (FetchMode.SELECT)
private Map<String, String> metadataItems = new HashMap<String, String>();
It's generally not a good idea to enforce eager fetching in the mapping - it's better to specify eager joins in appropriate queries (unless you're 100% sure that under any and all circumstances your object won't make sense / be valid without that collection being populated).
The reason you're getting duplicates is because Hibernate internally joins your root and collection tables. Note that they really are duplicates, e.g. for 2 SynonymMappings with 3 collection elements each you would get 6 results (2x3), 3 copies of each SynonymMapping entity. So the easiest workaround is to wrap results in a Set thereby ensuring they're unique.
I have faced this problem and I solved it using
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
This clears out the duplicates which are caused by the join made to the child tables.
You could use a SELECT DISTINCT (Hibernate Query Language) clause as follows
SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values
DISTINCT clause removes duplicate references in Hibernate.
Although both component and value-type collection has its lifecycle bound to the owning entity class, you should declare them in select clause in order to retrieve them. (LEFT JOIN FETCH synonym.values)
ChssPly76's answer is another approach, but does not forget override equals and hashcode method according to Set semantic
regards,
Instead of FetchMode.SELECT with N+1 queries it is better using BatchSize e.q. #BatchSize(size = 200).
DISTINCT and Criteria.DISTINCT_ROOT_ENTITY doesn't help, if you have to fetch more than 1 association. For this case see other solutions: https://stackoverflow.com/a/46013654/548473
I have achieved it via simply add
session.createCriteria(ModelClass.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
This help to remove duplicate.