46

In JPA, is there any way you can replicate Hibernate's saveOrUpdate behavior,

saveOrUpdate

public void saveOrUpdate(Object object)
                  throws HibernateException

    Either save(Object) or update(Object) the given instance, depending upon resolution of the unsaved-value checks (see the manual for discussion of unsaved-value checking).

    This operation cascades to associated instances if the association is mapped with cascade="save-update".

    Parameters:
        object - a transient or detached instance containing new or updated state 
    Throws:
        HibernateException
    See Also:
        save(Object), update(Object)

which essentially checks to see if the object already exists in the database and either updates that object as need be or saves a new instance of the object.

JPA transcationless reads are nice, but I am really missing this method from Hibernate. How do experienced JPA developers handle this?

James McMahon
  • 48,506
  • 64
  • 207
  • 283

2 Answers2

37

Try using the EntityManager.merge method - this is very similar.

There is an excellent description of the differences in Xebia's blogpost: "JPA Implementation Patterns: Saving (Detached) Entities."

Pablojim
  • 8,542
  • 8
  • 45
  • 69
  • 7
    The big take away for me here is "When updating an existing entity, we do not invoke any EntityManager method; the JPA provider will automatically update the database at flush or commit time." – James McMahon Jul 17 '09 at 11:07
  • 2
    Link is dead. Here is the new one: http://blog.xebia.com/jpa-implementation-patterns-saving-detached-entities/ – Dmitry Sep 14 '17 at 08:42
  • This page doesn't exist anymore, 404 page not found – Yashpal Jan 06 '22 at 07:05
  • 1
    https://xebia.com/jpa-implementation-patterns-saving-detached-entities/ ;-) – emeraldjava Jan 20 '22 at 12:00
5

The problem with the method outlined in the article that Pablojim linked to, is that it doesn't handle auto generated primary keys very well.

Consider the creation of a new ORM entity object, you can give this the same data as an existing row in the database table, but unless I am mistaken, the entity manager does not recognize them as the same row until they have the same primary key, which in a entity that uses auto generated keys, you can't get until you go up to the database.

Here is my current work around for that situation;

/**
 * Save an object into the database if it does not exist, else return
 * object that exists in the database.
 *
 * @param query query to find object in the database, should only return
 * one object.
 * @param entity Object to save or update.
 * @return Object in the database, whither it was prior or not.
 */
private Object saveOrUpdate(Query query, Object entity) {
    final int NO_RESULT = 0;
    final int RESULT = 1;

    //should return a list of ONE result, 
    // since the query should be finding unique objects
    List results = query.getResultList();
    switch (results.size()) {
        case NO_RESULT:
            em.persist(entity);
            return entity;
        case RESULT:
            return results.get(0);
        default:
            throw new NonUniqueResultException("Unexpected query results, " +
                    results.size());
    }
}
James McMahon
  • 48,506
  • 64
  • 207
  • 283
  • 1
    merge and saveOrUpdate are superficially similar, but not the same. The semantics have important differences. – skaffman Jul 17 '09 at 11:31
  • @skaffman, do you think my approach is flawed? Could you throw some constructive criticism my way if it is. I am a babe in the woods for this JPA stuff. – James McMahon Jul 17 '09 at 11:33
  • Just found this old thread, and I am implementing something like this solution except with the new Java ENUMS. Thanks. – oberger Mar 04 '12 at 20:08