14

I have a main thread where I get an object from database, then close the EntityManager, then put the object into a queue. A worker thread makes some business with the object, then puts it into a finished queue. Then the main thread gets the object from the finished queue and merges the object:

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
// Print the primary key ID of the object,
// it is NOT empty yes it exists in the db.
em.merge(myObject);
em.getTransaction().commit();
em.close();

I confirm the primary key ID of the object by printing it before merging, and yes it exists in the database. Unfortunately it throws a duplicate key exception from MySQL. How? Shouldn't JPA know that the object has an ID and update it, instead of inserting?

The SQL statement that is mentioned in the exception is an INSERT statement, not UPDATE.

The primary key of the entity is below:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "COMPANYID")
private long companyId;

SOLVED

I was setting a List field of the object to the List field of another newly created object. Then, for some reason that I don't understand, EclipseLink thinks that the object is a new object. God knows why. This JPA stuff is extremely counter-intuitive. You have to be truly an expert to use it. Otherwise it is totally useless because anytime you can mess it up. In practice the total headache and time loss it creates is much more than its advantages!

Anyway, I solved the problem by adding the elements of the other object to the old object as follows:

oldObject.getHistories().clear();
for (History history : newObject.getHistories()) {
    history.setCompany(oldObject);
    oldObject.getHistories().add(history);
}

Now EclipseLink correctly UPDATES instead of inserting new. I will be happy if someone can explain why this behavior?

  • The explanation for this sort of issues is usually easy, but you'll have to post the relevant mappings and explain what are you merging and which list are you talking about. – Dragan Bozanovic Mar 26 '16 at 23:58
  • I recently experienced the same issue: my Entity became detached unexpectedly after storing it in a POJO field. It seems you have to be very careful with how you store and pass around Entity objects so that they don't become detached... :/ – Matsu Q. Jul 21 '17 at 17:57

1 Answers1

2

This is happening because your EntityManager does not know about the object myObject.

For your code to work you should do something like this:

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Object myObject = em.findById(someId);
em.getTransaction().commit();
object.setName("yourNewName");
.....................
//And now run 
em.getTransaction().begin();
em.merge(myObject);
em.getTransaction().commit();
em.close();

Please note that I close the EntityManager only after I have done all my transactions needed to the database. And myObject is exactly the one that I recieved from my em.findById, I do not simply create a new object, because that will not work.

This is a tutorial that maybe can help you understand a bit more.(you have in the right a playlist with more videos about JPA ).

Mihai
  • 1,212
  • 16
  • 25
  • I did not say to leave the EntityManager open for a log time, I said that it has to be the same entity manager that makes the get and the update statement. If it is a new thread, once you have the object that you have to store, create an entity manager, get the object from db, replace the values with the values you have in the object from thread, and save it back in the db. – Mihai Mar 26 '16 at 22:42
  • Or you can create a custom querry yourself where you write the update statement yourself – Mihai Mar 26 '16 at 22:42