Spring + Hibernate:A Different Object with the Same Identifier Value Was Already Associated with the Session

Hibernate Error: a different object with the same identifier value was already associated with the session

Most probably its because the B objects are not referring to the same Java C object instance. They are referring to the same row in the database (i.e. the same primary key) but they're different copies of it.

So what is happening is that the Hibernate session, which is managing the entities would be keeping track of which Java object corresponds to the row with the same primary key.

One option would be to make sure that the Entities of objects B that refer to the same row are actually referring to the same object instance of C. Alternatively turn off cascading for that member variable. This way when B is persisted C is not. You will have to save C manually separately though. If C is a type/category table, then it probably makes sense to be that way.

Hibernate Spring JPA Session: A different object with the same identifier value was already associated with the session

You should not update entities in that way, this only fits for creating new entities.

productRepo.save(prod)

Correct way is to first load entity by id (this would attach object to session), update it and then save

Product existing = productRepo.findById(prod.getId());
existing.getStock().setCount(+prod.getStock().getCount());
productRepo.save(existing);

But in your code you receive "unmanaged" (that is not attached to hibernate session) object with existing ID (!), update it and save. Of course it would give an error

Spring Data JPA - A different object with the same identifier value was already associated with the session

It was all about the generator
So you must declare the same value on both allocationSize (Hibernate) and sequence increment by (DB)
https://stackoverflow.com/a/19036625/12832902

Spring + Hibernate : a different object with the same identifier value was already associated with the session

Use merge(). The exception means that the current session is already aware of the entity you are passing. If not, check how you have overridden hashCode() and equals() - it should return different values for different entities.

Hibernate Error: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

I have had this error many times and it can be quite hard to track down...

Basically, what hibernate is saying is that you have two objects which have the same identifier (same primary key) but they are not the same object.

I would suggest you break down your code, i.e. comment out bits until the error goes away and then put the code back until it comes back and you should find the error.

It most often happens via cascading saves where there is a cascade save between object A and B, but object B has already been associated with the session but is not on the same instance of B as the one on A.

What primary key generator are you using?

The reason I ask is this error is related to how you're telling hibernate to ascertain the persistent state of an object (i.e. whether an object is persistent or not). The error could be happening because hibernate is trying to persist an object that is already persistent. In fact, if you use save hibernate will try and persist that object, and maybe there is already an object with that same primary key associated with the session.

Example

Assuming you have a hibernate class object for a table with 10 rows based on a primary key combination (column 1 and column 2). Now, you have removed 5 rows from the table at some point of time. Now, if you try to add the same 10 rows again, while hibernate tries to persist the objects in database, 5 rows which were already removed will be added without errors. Now the remaining 5 rows which are already existing, will throw this exception.

So the easy approach would be checking if you have updated/removed any value in a table which is part of something and later are you trying to insert the same objects again

spring jpa - a different object with the same identifier value was already associated with the session

By seeing as much code you have provided this types of problem comes because the objects are not referring to the same Java object instance.This can happen when you have used same session object for read & write Or if you are putting same object in single session. They are referring to the same row in the database (i.e. the same primary key) but they're different copies of it.So what is happening is that the session, which is managing the entities would be keeping track of which Java object corresponds to the row with the same primary key.

I would recommend you to try below given code.

1- Just set cascade to MERGE, that should work for you.

OR

2- @GeneratedValue(strategy = GenerationType.SEQUENCE) OR Other GenerationType

A different object with the same identifier using Spring data jpa with Hibernate

Before full explaination a little note: try to post code that actually compiles and works as advertised.

  • Your main() does not compile,
  • you dont set up full relation between Parent and Child.
  • Also try to explicitely demarcate transactions in the posted example.

How your code works

You are calling save on a repository. Underneath, this method calls entityManager.merge() as you have set an id yourself. Merge calls SQL Select to verify if the object is there, and subsequently calls SQL insert or update for the object. (The suggestions that save with the object with id that exists in db are wrong)

  • In the first run, the object is not there.

    • you insert parent
    • merge is cascaded and you insert child (lets call it childA)
  • In the second run

    • merge selects parent (with childA)
    • We compare if new parent is already in the session.
      This is done in SessionImpl.getEntityUsingInterceptor
    • parent is found
    • merge is cascaded to the child
    • again, we check if the object is already in the session.
    • Now the difference comes:
    • Depending on how you set up the relation between child and parent, the child may have an incomplete PK (and rely on filling it from the relation to parent annotated with @MapsId). Unfortunately, the entity is not found in the session via the incomplete PK, but later, when saving, the PK is complete, and now, you have 2 confilicting objects with the same key.

To solve it

Child child = new Child();
child.parent = parent;
child.childPK.cid = 1;
child.childPK.parentPk = 1;

This also explains why the code works when you change the PK of Child to a long - there is no way to screw it up and have an incomplete PK.

NOTE

The solution above makes mess with orphans.

I still think that the original solution is better as the orphans are removed.
Also, adding updated soution to original solution is a worthwhile update.
Removing entire list and re-inserting it is not likely perform well under load.
Unfortunalely it removes the list on the first merge of the parent, and re-adds them on the second merge of the parent. (This is why clear is not needed)

Better still, just find the parent entity and make the updates on it (as other answers suggest).

Even better, try to look at the solution and add / replace only specific children of the parent, not lookig at the parent and its children ollection. This will be likely most performant.

Original Solution

I propose the following (note that total replacement of the chilren list is not allowed, as it is a hibernate proxy).

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Child> children = new ArrayList<>();
@SpringBootTest
public class ParentOrphanRepositoryTest {

@Autowired
private ParentOrphanRepository parentOrphanRepository;

@Test
public void testDoubleAdd() {
addEntity();
addEntity();
}

@Transactional
public void addEntity() {
Parent parent = new Parent();
parent.pid = 1;
parent.name = "Parent 1";

parent = parentOrphanRepository.save(parent);

Child child = new Child();
List<Child> childList = new ArrayList<>();

child.parent = parent;
child.childPK.cid = 1;
child.name = "Child 1";
childList.add(child);

// parent.children.clear(); Not needed.
parent.children.addAll(childList);
parentOrphanRepository.save(parent);
parentOrphanRepository.flush();
}
}


Related Topics



Leave a reply



Submit