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

32

124

I essentially have some objects in this configuration (the real data model is a bit more complex):

  • A has a many-to-many relationship with B. (B has inverse="true")
  • B has a many-to-one relationship with C. (I have cascade set to "save-update")
  • C is a kind of type/category table.

Also, I should probably mention that the primary keys are generated by the database on save.

With my data, I sometimes run into problems where A has a set of different B objects, and these B objects refer to the same C object.

When I call session.saveOrUpdate(myAObject), I get a hibernate error saying: "a different object with the same identifier value was already associated with the session: C". I know that hibernate can't insert/update/delete the same object twice in the same session, but is there some way around this? This doesn't seem like it would be that uncommon of a situation.

During my research of this problem, I have seen folks suggest the use of session.merge(), but when I do that, any "conflicting" objects get inserted into the database as blank objects with all values set to null. Clearly that isn't what we want.

[Edit] Another thing I forgot to mention is that (for architectural reasons beyond my control), each read or write needs to be done in a separate session.

Christophe answered 26/4, 2013 at 23:35 Comment(1)
See if this answer helps you..Toweling
A
119

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.

Arjuna answered 26/4, 2013 at 23:57 Comment(5)
Thanks jbx. As you said, it turns out that the B objects are referring to multiple C instances in memory. Essentially what is happening is that one part of my program is reading in C, and attaching it to B. Another part is loading a different B with the same C from the database. Both are being attached to A which triggers the error on save. I've set the <pre>cascade</pre> for the B->C relationship to "<pre>none</pre>," but I'm still getting the same error. In many-to-one or one-to-many relationships, is there a way to tell Hibernate only to change the foreign key and not worry about the rest?Christophe
Does C's primary key have any ID generation strategy? Like a sequence-generator or something similar?Arjuna
Yes, each has its own sequence in the database. As you mentioned, cascading turned out to be the issue. We turned off cascadeing for the type tables, and for the others we used "merge" cascade, which allowed us to call merge() without creating all those null rows. I've marked your answer accordingly, thanks!Christophe
I have used merge() instead of saveOrUpdate() and BOOM! it works :)Expiration
@AmirhoseinAl what does this have to do with the answer? ask a new question about your problem and describe it wellArjuna
S
41

Just set cascade to MERGE, that should do the trick.

Slavonic answered 8/8, 2015 at 21:21 Comment(1)
This works for me ` session.getDatabaseSession().merge(op)` thank you.Kimbell
P
21

You only need to do one thing. Run session_object.clear() and then save the new object. This will clear the session (as aptly named) and remove the offending duplicate object from your session.

Percussionist answered 18/9, 2014 at 7:55 Comment(2)
How do I get a hold of the session_object that has the clear() method?Ceramic
session.clear() also removes all other (non-offending) objects from the session. So this is not a good option.Vaucluse
D
20

I agree with @Hemant Kumar, thank you very much. According his solution, I solved my problem.

For example:

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Person.java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

This code always make mistake in my application: A different object with the same identifier value was already associated with the session, later I found out that I forgot to autoincrease my primary key!

My solution is to add this code on your primary key:

@GeneratedValue(strategy = GenerationType.AUTO)
Dragonnade answered 25/1, 2017 at 8:13 Comment(2)
After adding the @GeneratedValue annotation, you may need to increase the connection pool size. Also, GenerationType.IDENTITY worked for me, otherwise, it was throwing "hibernate_sequence does not exist"Klepac
Should I set any of these strategies if using an @EmbeddedId?Wichita
S
11

This means you are trying to save multiple rows in your table with the reference to the same object.

check your Entity Class' id property.

@Id
private Integer id;

to

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
Shlomo answered 23/3, 2018 at 7:19 Comment(1)
not the case with question as per description givenFm
C
6

Transfer the task of assigning the object ID from Hibernate to the database by using:

<generator class="native"/>

This solved the problem for me.

Caducous answered 24/7, 2014 at 17:26 Comment(0)
C
5

Another case when same error message can by generated, custom allocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

without matching

alter sequence par_idpar_seq increment 20;

can cause constraint validation during insert(that one is easy to understand) or ths "a different object with the same identifier value was already associated with the session" - this case was less obvious.

Coronograph answered 8/10, 2020 at 14:29 Comment(1)
Had this problem, adding allocationSize = 1 , fixed it because the sequence generator in db was defined with increment of 1 and hibernate's allocationSize default is 50 (for some weird reason).Subheading
Y
4

One way to solve the above problem will be to override the hashcode().
Also flush the hibernate session before and after save.

getHibernateTemplate().flush();

Explicitly setting the detached object to null also helps.

Yardman answered 7/10, 2014 at 5:40 Comment(0)
H
3

Add the annotation @GeneratedValue to the bean you are inserting.

Heloise answered 26/3, 2015 at 12:1 Comment(1)
Thanks! This was exactly my problemIngeingeberg
A
2

Just came across this message but in c# code. Not sure if it's relevant (exactly the same error message though).

I was debugging the code with breakpoints and expanded some collections through private members while debugger was at a breakpoint. Having re-run the code without digging through structures made the error message go away. It seems like the act of looking into private lazy-loaded collections has made NHibernate load things that were not supposed to be loaded at that time (because they were in private members).

The code itself is wrapped in a fairly complicated transaction that can update large number of records and many dependencies as part of that transaction (import process).

Hopefully a clue to anyone else who comes across the issue.

Autolithography answered 18/6, 2014 at 9:54 Comment(0)
S
2

Find the "Cascade" atribute in Hibernate and delete it. When you set "Cascade" available, it will call other operations (save, update and delete) on another entities which has relationship with related classes. So same identities value will be happened. It worked with me.

Sillsby answered 13/4, 2017 at 13:51 Comment(0)
M
1

I had this error few days a go and I sped too many time on fixing this error.

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

Before i get this error , I didn't have mentioned ID generation type on the OrderDetil Object. when without generating Orderdetails' id it keeps Id as 0 for every OrderDetail objects. this what #jbx explained. Yes it is the best answer. this one example how it happens.

Muddle answered 27/6, 2015 at 18:45 Comment(0)
A
1

Try to place the code of your query before. That fix my problem. e.g. change this:

query1 
query2 - get the error 
update

to this:

query2
query1
update
Avis answered 10/2, 2017 at 7:40 Comment(0)
M
1

In my case only flush() did not work. I had to use a clear() after flush().

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}
Managua answered 19/3, 2018 at 13:0 Comment(0)
C
1

Make Sure, your entity have same Generation Type with all Mapped Entitys

Ex : UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

Module :

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}

Main Entity with Mapping

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}

Chon answered 19/11, 2019 at 9:3 Comment(0)
J
1

I had this same error with JPA repository.

The save operation returns a completely different instance. Use that one.

In JPA repository, the javadoc of CrudRepository.save() operation states:

Use the returned instance for further operations as the save operation might have changed the entity instance completely.

So, instead of:

repository.save(fooEntity);
buzEntity.setFoo(fooEntity);

Use the returned instance which is the one that is now merged into the EntityManager, for further operations:

Foo mergedFooEntity = repository.save(fooEntity);
buzEntity.setFoo(mergedFooEntity);
Jacqulynjactation answered 9/6, 2023 at 13:35 Comment(0)
L
0

you might not be setting the identifier of the object before calling update query.

Lonee answered 11/11, 2013 at 9:59 Comment(1)
If he was not he wouldn't have this problem. The problem is he has two objects with the same identifier.Bobbitt
E
0

I met the problem because of the primary key generation is wrong,when I insert a row like this:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

I change the id generator class to identity

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>
Erikaerikson answered 14/12, 2017 at 6:28 Comment(0)
C
0

If you use EntityRepository then use saveAndFlush instead of save.

Cowgill answered 20/11, 2018 at 14:14 Comment(0)
D
0

If left an expressions tab in my IDE open which was making a hibernate get call on the object causing this exception. I was trying to delete this same object. Also I had a breakpoint on the delete call which seems to be necessary to get this error to happen. Simply making another expressions tab to be the front tab or changing the setting so that the ide does not stop on breakpoints solved this problem.

Denude answered 29/4, 2019 at 20:12 Comment(0)
L
0

In addition to all the previous answers, a possible fix to this problem in a large scale project, if your using a Value Object for your classes don't set the id attribute in the VO Transformer class.

Lactescent answered 19/1, 2020 at 10:44 Comment(0)
S
0

The reason for this issue is you have have different copies of objects referring into same raw in your child table, so spring trying to treat your object as new object but while saving it identifies there is a raw with same primary key. So it gives above error.

Best solution for this issue is to load the whole object (parent entity with child entities) from DB (you already know the primary key of parent object), then update values in the object loaded from DB from your new object(which you were trying to save) and then save the object you loaded from the DB which has new values.

This will update your values in the DB without giving above error.

PS- Do not need to update ids as they already exist in object loaded from DB, update only the values need to be changed

Spindlelegs answered 4/8, 2021 at 21:13 Comment(0)
E
0

Another way to solve this if you are using spring data:

  • replace calls to entityManager.persist() with calls to repository.save(), and
  • replace calls to entityManager.query().getResultList() etc with calls to repository.findBy...

This way, spring data keeps track of the objects. It enables multiple get and persist calls.

Ey answered 23/12, 2021 at 9:42 Comment(0)
G
0

instead of just @Id try

@Id
@GeneratedValue(strategy = GenerationType.AUTO)

it worked for me

Gavrielle answered 2/2, 2022 at 10:7 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Elman
C
0

In my case, I had OneToOne relationship and after saving one row with a foreign key, attempt to save another row with the same foreign key throw the same exception. It means that requirement was not OneToOne relationship, but should be ManyToOne. So I have changed it to ManyToOne and it started working.

Cockroach answered 17/2, 2022 at 6:3 Comment(0)
W
0

This error commonly occurs because you are violating a column of unique type or primary key when trying to insert repeated data.

Wolfie answered 14/3, 2022 at 18:37 Comment(0)
R
0

make sure primary key is different for every entity.

primary key must be unique

Radiolocation answered 23/5, 2022 at 9:25 Comment(0)
T
0

Faced this error too. In my case problem was in row which has somehow zero id.

Talcahuano answered 31/3, 2023 at 12:33 Comment(0)
G
0

not using saveorupdate, as the experts say, is an aberration, using save and update or delete separately for Hibernate 5, for 6, this function is no longer used flush an clear

Gemmation answered 18/5, 2023 at 22:35 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Elman
P
0

Faced the same error, In my case B has a bi~directional one-to-many relationship with A and a Cascade.ALL.
The cause of this error in my case was me trying to insert B(I update A which auto saves B due to Cascade.ALL) to an existing row.
To solve this I had to check if B was updated then relate it to A and update or else don't relate B to A then update(Add an if condition to do the checks). This solve the issue in my case.

Pierette answered 10/6, 2023 at 5:56 Comment(0)
C
0

You can try session.clear() before transaction of session what you want. Reason of this error is the session have got conflict.

Cramer answered 24/2 at 8:37 Comment(0)
B
-4

just commit current transaction.

currentSession.getTransaction().commit();

now you can begin another Transaction and do anything on entity

Broken answered 25/2, 2020 at 7:6 Comment(1)
DONT use this approach. a commit should be done after you finished your whole work, since the transaction handling is very expensive.Valence

© 2022 - 2024 — McMap. All rights reserved.