68

So far, my preference has been to always use EntityManager's merge() take care of both insert and update. But I have also noticed that merge performs an additional select queries before update/insert to ensure record does not already exists in the database.

Now that I am working on a project requiring extensive (bulk) inserts to the database. From a performance point of view does it make sense to use persist instead of merge in a scenario where I absolutely know that I am always creating a new instance of objects to be persisted?

phewataal
  • 1,067
  • 3
  • 12
  • 20

3 Answers3

72

It's not a good idea using merge when a persist suffices - merge does quite a lot more of work. The topic has been discussed on StackOverflow before, and this article explains in detail the differences, with some nice flow diagrams to make things clear.

Community
  • 1
  • 1
Óscar López
  • 215,818
  • 33
  • 288
  • 367
  • 2
    This "answer" is equivalent to flagging the question as duplicate and adding a link to the article to this or the duplicated question. – Karl Richter Jun 16 '17 at 14:55
9

I would definitely go with persist persist() if, as you said:

(...) I absolutely know that I am always creating a new instance of objects to be persisted (...)

That's what this method is all about - it will protect you in cases where the Entity already exists (and will rollback your transaction).

Piotr Nowicki
  • 16,436
  • 8
  • 54
  • 79
  • Is it a good coding practice to do something like : try { em.persist(xxx); } catch (RuntimeException re) { try { xxx = em.merge(xxx); } catch (Exception e) { throw e; } } – phewataal Dec 12 '11 at 19:18
  • 7
    Despite that catching `RuntimeException` is definitely not a good practice (but I'll assume you meant `EntityExistsException`) I wouldn't call it an universal *good coding practice*. It depends on your requirements. If the requirements says that object **must be persisted** and it **must not exist** before this action occurs - I would definitely not try to do merge after catching exception. Moreover, if you use JTA entity manager, your transaction will be already marked for rollback at this time. – Piotr Nowicki Dec 12 '11 at 22:16
  • can you please explain what do you mean by "Moreover, if you use JTA entity manager, your transaction will be already marked for rollback at this time"? I am using CMT for EJB3 and I was able to verify that as long as I eat the exception within a method boundary, no transaction is rolled back. – phewataal Jan 20 '12 at 16:54
  • 2
    @phewataal http://docs.oracle.com/javaee/6/api/javax/persistence/EntityExistsException.html – Piotr Nowicki Jan 20 '12 at 16:58
  • So what with this EntityExistsException and merging after persisting method? When persist() throws an RuntimException does it mean that entity has been added to persistence context , so EntityExistsException schould be thrown during merging? Or maybe EntityExistsException would not be thrown, because transaction has been rolled back due to RuntimeException and new context had been created? – Damian May 18 '12 at 19:22
  • 2
    I guess that when you invoke `persist` on an entity that already exists, you'll get `EntityExistsException` and because of the JPA implementor this transaction will be rolled back (not because it's a runtime exception.). In other words - I **think** (not sure) that even if you try/catch this exception, the tx will still be marked for rollback. The `merge` will never throw `EntityExistsException`. – Piotr Nowicki May 18 '12 at 20:16
2

If you're using the assigned generator, using merge instead of persist can cause a redundant SQL statement, therefore affecting performance.

Also, calling merge for managed entities is also a mistake since managed entities are automatically managed by Hibernate and their state is synchronized with the database record by the dirty checking mechanism upon flushing the Persistence Context.

To understand how all this works, you should first know that Hibernate shifts the developer mindset from SQL statements to entity state transitions.

Once an entity is actively managed by Hibernate, all changes are going to be automatically propagated to the database.

Hibernate monitors currently attached entities. But for an entity to become managed, it must be in the right entity state.

First, we must define all entity states:

  • New (Transient)

    A newly created object that hasn’t ever been associated with a Hibernate Session (a.k.a Persistence Context) and is not mapped to any database table row is considered to be in the New (Transient) state.

    To become persisted we need to either explicitly call the EntityManager#persist method or make use of the transitive persistence mechanism.

  • Persistent (Managed)

    A persistent entity has been associated with a database table row and it’s being managed by the current running Persistence Context. Any change made to such entity is going to be detected and propagated to the database (during the Session flush-time). With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current Session flush-time.

  • Detached

    Once the current running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.

    To associate a detached entity to an active Hibernate Session, you can choose one of the following options:

    • Reattaching

      Hibernate (but not JPA 2.1) supports reattaching through the Session#update method. A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated with a given key (entity type and database identifier). An entity can be reattached only if there is no other JVM object (matching the same database row) already associated to the current Hibernate Session.

    • Merging

    The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database. The detached object instance will continue to remain detached even after the merge operation.

  • Removed

    Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call). A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.

To understand the JPA state transitions better, you can visualize the following diagram:

enter image description here

Or if you use the Hibernate specific API:

enter image description here

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788