17

We are using Toplink implementation of JPA + Spring + EJB. In one of our EJBs we have something like this:

public void updateUser(long userId, String newName){
    User u = em.get(User.class, userId);
    u.setName(newName);
    // no persist is invoked here
}

So, basically this updateUser() method is supposed to update the name of a user with the given userId. But the author of this method forgot to invoke em.persist(u).

And the strangest thing is that it works fine. How can it be? I was 100% sure that without invoking em.persist() or em.merge() there is no way that changes could have been saved into database. Could they? Is there any scenario when this could happen?

Alexander Pozdneev
  • 1,289
  • 1
  • 13
  • 31
anthony
  • 283
  • 1
  • 3
  • 12

1 Answers1

17

You're working with a managed entity. If the entity does not become detached because its entity manager is closed, all changes done to the entity are reflected to the database when the session is flushed/closed and the transaction commited.

From the Java EE tutorial:

The state of persistent entities is synchronized to the database when the transaction with which the entity is associated commits.

Edit for clarity and explanation: So there are three distinct modes that an entity could be in during its lifecycle:

  • Unsaved: The entity has been instantiated, but persist() has not been called yet.
  • Managed: The entity has been persisted using persist(), or loaded from the database, and is associated with an entity manager session. All changes to the entity are reflected to the database when the entity manager session is flushed.
  • Detached: The entity's entity manager session was closed. Changes to the entity will not be reflected to the database automatically, but can be merged explicitly using the merge() command.
Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
Henning
  • 16,063
  • 3
  • 51
  • 65
  • Thanks. This helped a lot! I always thought that invoking em.persist() is required, even if object is associated with current EntityManager. – anthony Nov 24 '09 at 18:21
  • Weird: In my setup, I have a reference to a managed entity. Then I change this entity's state. Then I call `entityManager.flush()`. In case this is running within a transaction, then the entity's changed state **is written** to database. In case this is running without a transaction, then the entity's changed state **is not written** to database. So is the following statement true?: *"There always has to be a transaction commiting or an `entityManager.persist(obj)` call in order to write the latest state of `obj` to database."*.... (I'm using JPA 2.0 with Hibernate (4.1.7.Final)) – Abdull Feb 13 '13 at 16:53
  • @Abdull, yes, the JPA spec does not define any autocommit behavior, so you need an ongoing transaction or nothing gets written to the database at all. – Henning Feb 13 '13 at 18:37
  • @Henning, okay thanks! Currently, your answer states: "*[...] All changes to the entity are reflected to the database when the entity manager session is flushed.*"...... What about rewriting it like: "*[...] If there is an ongoing transaction, all changes to the entity are reflected to the database when the entity manager session is flushed.*" .... (or are there even more subtleties involved?) – Abdull Feb 13 '13 at 19:37
  • @Abdull, it really says "when the session is flushed/closed *and the transaction commited*", so I think we have the transaction part covered, right? – Henning Feb 13 '13 at 20:09