Persistence units encapsulate the object-relational mapping to the database. This section is about how they encounter transactions, concurrency, and multiple requests.
Here are some helpful rules about javax.ejb.EntityManager
:
Prefer to inject
EntityManager
as a dependency in a Java EE application. Therefore, the application server takes care of its lifecycle and the responsibility to close the persistence unit. Use@PersistenceContext
.Do not cache or store in between requests: The
EntityManager
instance is not thread-safe.The
EntityManager
instance manually retrieved fromEntityManagerFactory
must be closed in a retrieval method request for that particular state lest it goes out of scope, escapes, and potentially becomes a memory leak.An
EntityManager
that represents a JTA data source must be associated with a transaction by calling thejoinTransaction()
method. This is particularly true in a Java EE 6 web container.Obtain
UserTransaction
through dependency injection and remember to callbegin()
andend()
to demarcate the transaction boundaries. The only exceptions to this rule are persistence units that are explicitly bound withSynchronization.UNSYNCHRONIZED
.Do use
EntityManagerFactory
to look up the persistence context in a Java SE application. The factory, definitely, is thread-safe.Do close the
EntityManager
instance with a final clause in Java SE application. Sadly the interface cannot yet be made bothAutoClosable
with Java SE 7 and also retrospectively compatible with Java SE 5 or 6.
Inside a Java EE environment,
EntityManager
is configured with a transaction by default. It can either be PersistenceContextType.TRANSACTION
or PersistenceContextType.EXTENDED
. The extended variety can only be injected into stateful session EJBs.
Transactions are demarcated on session EJB methods, which by default are set for TransactionAttribute.REQUIRED
. Developers can explicitly annotate methods with TranactionAttribute.SUPPORTS
or TranactionAttribute.MANDATORY
, but this is normally unnecessary. In a Java EE application, a transaction-able method in a session EJB will be associated with the EntityManager
object instance by default.
During operations in TRANSACTION mode, the application server will take responsibility for transactions in this scenario. The entities attached to the persistence unit are alive for the entire duration of the transaction. Once the method ends normally (or abnormally) the transaction is completed. The entities are detached from the persistence unit. When the entities are detached, then any changes to them are not synchronized to a database.
Transactions associated with entity managers can survive through nested invocations of other reference EJB calls, assuming that they permit and support transactions. This is called Transaction Propagation. All JPA entities remain intact and attached to the current entity manager.
Here is the table that summarizes the different cases of EntityManager
for manual operation, in a stateless and stateful session bean.
The abbreviation N/A stands for Not Applicable.
EntityManager
has methods that require an active transaction: persist()
, merge()
, remove()
, refresh()
, and flush()
. If there is no associated current transaction then these methods will raise a TransactionRequiredException
.
Stateful session beans are a special case of EntityManager
when they are used in the extended persistence unit situation PersistenceContextType.EXTENDED
. Entities remain attached and synchronized with the entity manager during interactions between the caller and the EJB.
In a web application this 1:1 relationship requires some thought and logic as the long-lived entities will stick around in a client specific cache of instances. In a long conversation, this behavior may be problematic, because the managed entities are not visible out of the transaction. The changes to the database are buffered inside the entity manager. In order to get the changes reflected in the database, it may be necessary to invoke the EntityManager.flush()
method at certain intervals. Of course, this will affect the ability to roll back to the beginning of the conversation and could be the bane of Java database development: lost updates seen by another user.
Because of the client specific cache of stateful session bean, we strongly recommend avoiding concurrency access with the same caller.
The managed entities will be attached until the caller invokes the stateful session bean's removal operation, which causes its dissociation from the caller and only then will the entity manager synchronize instance with the database and then detach the entities.
JPA supports both optimistic and pessimistic locking facilities. By default, JPA updates rows in a database table using optimistic locking.
Optimistic locking is a lenient strategy for dealing with concurrency access to shared entities held by EntityManager
. With this strategy, which tends to scale better than pessimistic locking, the application can retrieve the data, make some changes to it, and write it to the database. Data version validation is performed during the write phase to the database.
To provide optimistic locking of entity in JPA, annotate a field or property with the @Version
constraint. Preferably use an integer field. Here is an example:
@Entity @Inheritance @DiscriminatorColumn(name=""TRADE_TYPE"") @Table(name=""TRADE"") public abstract class Trade { @Id private long id; @Version private long version; // ... }
The persistence provider performs optimistic locking by comparing the value of the @Version
field in the entity instance with the VERSION
column from the database. Initially the provider reads the entity from the database and has a record of the current version. Therefore, the provider knows that another concurrent operation has successfully updated the entity when the values are different. If there is a difference in value during the write phase, EntityManager
will raise an OptimisticLockException
.
Adding this annotation on an integer value allows the provider to make a faster check on an entity's version without having to check every single field or property for equivalence. The persistence provider updates the @Version
field or property automatically. User code should not interfere with this arrangement.
Sometimes there is a strong business requirement for strict locking of database rows. JPA has support for pessimistic locking. An entity can be locked with a call to the
EntityManager.lock()
,
refresh()
, or
find()
methods. There are different types of locks available through the enumeration LockModeType
including the default optimistic. The enumerations are: NONE
, OPTIMISTIC
, OPTIMISTIC_FORCE_INCREMENT
, PESSIMISTIC_FORCE_INCREMENT
, PESSIMISTIC_READ
, PESSIMISTIC_WRITE
, READ
, WRITE
, and OPTIMISTIC_FORCE_INCREMENT
.
The underlying database itself must support pessimistic locking. In order to achieve pessimistic locking, the JPA providers take advantage of the database server features, such as database row locks and sometimes extended native SQL language statements: SELECT … FOR UPDATE
.
Each of the
LockModeType
enumerations has a trade-off. It is interesting that there are two types of pessimistic modes: reading and writing. Locking an entity with a pessimistic query could lead to performance bottlenecks as it prevents other concurrency operations from reading the particular row from the database.
Here is an example of a pessimistic write operation for a trade confirmation operation:
@Stateless public class TradeConfirmService { //... void confirmTrade( String tradeIdRef, String ref ) { Query query = entityManager.createNamedQuery( ""Trade.findTradeByIdRef"") .setParameter(""id"", tradeIdRef ); List<Trade> trades = query.getResultList(); Trade trade = trades.get(0); em.refresh( trade, PESSIMISTIC_WRITE ); trade.setConfirmFlag(true); trade.setTraderConfirmRef(ref); em.flush(); // optional } }
A trade is retrieved optimistically using a named query. It is pessimistically locked for the write operation with the
refresh()
call on the entity manager. New values are set on the trade instance and EntityManager
is flushed to update the database.