JPA EntityManager: merge() is trying to create a new row in the db - why?

15,027

Solution 1

I believe your problem is the way Play manages the JPA environment (and transactions).

Once you receive a request the framework immediately creates the JPA manager and a transaction. From that moment onwards all your Model entities are automatically linked to the manager.

Play facilitates working with this model in 2 ways:

  • You have to explicitly indicate that you want to save changes to an object (via save())
  • Transaction is committed automatically unless there is an exception or you flag it for rollback (JPA.setRollbackOnly())

By running "merge" you are trying to add to the Manager an entity which is already there, which causes the unique key exception. If you just load the entity from the cache, you will be able to modify and call save() once done, and it will work.

Solution 2

I think it may be a poblem with hashCode() and equals(). If there are not implemented correctly, a new entity my be inserted instead of updateing an existing one.

Solution 3

See What is the proper way to re-attach detached objects in Hibernate?. Merge tries to write the stale state to the db in order to overwrite possible other concurrent updates. The linked question mentions session.lock(entity, LockMode.NONE); as a possible solution, I haven't tried it though.

Solution 4

If authToken is not a primary key, then perhaps primary key of the User instance being merged doesn't match the primary key of its counterpart in the database, therefore merge() thinks that it's a new User and tries to insert it.

So, check the primary key of the User, perhaps it have been corrupted or lost somehow.

Solution 5

It's an entity definition problem; specifically with regard to primary key/ unsaved values.

The entity definition needs to be correct, for Hibernate to recognize it as 'already saved'. For example, having 'null' in a nullable version field can cause Hibernate to disregard any existing ID & regard it as unsaved.

This is a Hibernate question, not just JPA. JPA is the interface -- you're having trouble with a specific implementation.

Share:
15,027
sanity
Author by

sanity

I'm an entrepreneur and computer scientist, with a particular interest in Artificial Intelligence and Peer-to-Peer. My two most notable projects are Freenet and Revver (I'm founder and co-founder respectively). My current projects are a predictive analytics system called SenseArray, and a new approach to distributed computation called Swarm. You can find my personal blog here. While I've used C, C++, ML, Haskell, Prolog, Python, even Perl in the past, these days I do most of my programming in Java. I am gaining experience with Scala though and expect to become my primary language as it and its tools mature. I was honored to be asked by Scala's creator to be on the program committee for the first Scala workshop.

Updated on June 15, 2022

Comments

  • sanity
    sanity almost 2 years

    I'm using JPA through the Play Framework.

    I'm checking to see if a User object is cached, and if so I retrieve it and merge() it such that I can update fields and save the changes later:

    user = (User) Cache.get("user-auth-" + sessionAuthToken);
    if (user != null) {
        user = user.merge();  // I believe this is the same as EntityManager.merge()
    }
    

    However when I do this I get the following error:

    PersistenceException occured : 
      org.hibernate.exception.ConstraintViolationException: 
        could not insert: [models.User]
    ...
    Caused by: com.mysql.jdbc.exceptions.jdbc4.
       MySQLIntegrityConstraintViolationException: 
         Duplicate entry '1235411688335416533' for key 'authToken'
    

    It seems like its trying to insert a new user, even though this user should be, and is already in the database. Why would merge() do that?

    Or perhaps I'm going about this entirely the wrong way - advice would be appreciated.

  • Christian Kuetbach
    Christian Kuetbach over 13 years
    This would mean, you can't compare or store two unsafed entites within a set, because they would be euqal unless you generate a UUID in the object constructor. (But this would not lead to your problem)