I have the code from below and wondering how does JPA know to persist this update. I expected an em.merge() to be needed in order to perform the update. Is this actually safe ?
@Stateless
class User {
...
public void generateRandomNicknames() {
List<UserEntity> users = em.createNamedQuery("UserEntity.getAllUsers", UserEntity.class)
.getResultList();
for (UserEntity user : users) {
user.setNickname(generateRandomNickname());
}
// em.merge(user) or em.persist(user) not needed ?
}
}
In short: Managed entities are generally synchronized with the database on transaction commit. The JPA implementation is responsible for tracking changed managed entities and update the database. In your case, calling user.setNickname(...)
will notify JPA that this specific user object is dirty. And a transaction is by default active on calling business methods of a Session EJB.
Note that the above is the default behavior that can be altered by configuration!
Longer story, with references from the JEE 8 sub-specs (the question is about JEE 6, these still apply, although in different section numbers):
(JPA 2.1) Section 3.2.4: Synchronization to the Database
The state of persistent entities is synchronized to the database at transaction commit. This synchronization involves writing to the database any updates to persistent entities and their relationships as specified above.
[...]The persistence provider runtime is permitted to perform synchronization to the database at other times as well when a transaction is active and the persistence context is joined to the transaction. The flush method can be used by the application to force synchronization.
There are other interesting details in this chapter, but note that merge()
does NOT have to be called, for a managed entity to be saved at the end of the transaction.
Which brings us to the next detail, the transaction. Since this is a method of a Session EJB, it is by default run in the context of a transaction:
(EJB core 3.2) Section 8.3.6: Specification of a Bean’s Transaction Management Type
By default, a session bean or message-driven bean has container managed transaction demarcation if the transaction management type is not specified. [...]
(EJB core 3.2) Section 8.3.7: Specification of the Transaction Attributes for a Bean’s Methods
The Bean Provider of an enterprise bean with container-managed transaction demarcation may specify the transaction attributes for the enterprise bean’s methods. By default, the value of the transaction attribute for a method of a bean with container-managed transaction demarcation is the REQUIRED transaction attribute, and the transaction attribute does not need to be explicitly specified in this case.
An important detail is that the entities are actually managed. This is, in this case, because they are returned from JPA itself and because of how the JPQL query "UserEntity.getAllUsers"
is structured. In general entities could be unmanaged or detached, in which case calling merge()
or persist()
would have been necessary.