The Java Persistence API (informally referred
to as JPA) provides a plain old Java object (POJO)-based persistence model
for Java EE and Java SE applications. It handles the details of how relational data is mapped to Java objects, and it
standardizes Object/Relational (O/R) mapping. The latest update to JPA,
Java Persistence 2.0, adds a number of new
features such as additional O/R mapping functionality and new query language capabilities. Another area that
has been enhanced in JPA 2.0 is locking and concurrency.
This Tech Tip highlights the new locking and concurrency features in JPA 2.0 and provides an application that demonstrates these capabilities.
Locking and Concurrency
Locking is a technique for handling database transaction concurrency. When two or more database transactions concurrently
access the same data, locking is used to ensure that only one transaction at a time can change the data.
There are generally two locking approaches: optimistic and pessimistic. Optimistic locking assumes that there
will be infrequent conflicts between concurrent transactions, that is, they won't often try to read and change the
same data at the same time. In optimistic locking, the objective is to give concurrent transactions a lot of freedom
to process simultaneously, but to detect and prevent collisions. Two transactions can access
the same data simultaneously. However, to prevent collisions, a check is made to detect any changes made to the data
since the data was last read.
Pessimistic locking assumes that transactions will frequently collide. In pessimistic locking, a transaction that
reads the data locks it. Another transaction cannot change the data until the first transaction commits the read.
Optimistic locking works best for applications where concurrent transactions do not conflict. Pessimistic locking
works best where concurrent transactions do conflict.
With JPA it is possible to lock an entity. This allows you to control when, where, and which kind of locking to use
for an entity. Recall that in JPA, an entity is a lightweight persistence domain object. Typically, an entity represents
a table in a relational database, with each entity instance corresponding to a row in that table.
Locking Support in JPA 1.0
JPA 1.0 only supports optimistic read or optimistic write locking. In this support, any transaction can read
and update an entity. However, when a transaction commits, JPA checks the version
attribute of the entity
to determine if it was updated since the entity was last read. If the version
attribute was updated since
the entity was last read, JPA throws an exception. The advantage of this approach is that no database locks are held.
This can result in better scalability than for pessimistic locking. The disadvantage of this approach is that the user
or application must refresh and retry failed updates.
A versioned entity is marked with the @Version
annotation, as illustrated in the following code
snippet:
and its corresponding database schema has a version column, such as that created by the following SQL statement:
The version
attribute can be an int, short, long, or timestamp. It is incremented when a transaction
successfully commits. This results in an SQL operation such as the following:
Figure 1 illustrates optimistic locking.
![]() |
Figure 1. Optimistic Locking |
Here, two concurrent transactions attempt to update Part p
. Transaction 1 commits first. In response,
JPA increments the version
attribute for the p
entity. When Transaction 2 commits, JPA
throws an OptimisticLockException
because the version
attribute for the p
entity
is higher than it was when Transaction 2 last read the p
entity. As a result, Transaction 2 is rolled back.
You can further control the way JPA manages locking on a versioned entity by specifying a lock mode. You do this through
the lock()
method of the EntityManager
class. Here is the method signature:
The first method parameter is the entity instance that needs to be locked in the transaction. The second method parameter
is the lock mode.
In JPA 1.0, the lock mode value could only be one of the following:
READ
. In this case, the JPA entity manager performs the optimistic locking operations as previouslyOptimisticLockException
and rolls back the transaction.WRITE
. In this case, the entity manager performs the same optimistic locking operations as forAdditional Locking Support in JPA 2.0
JPA 2.0 adds five new lock modes. Two of these are used for optimistic locking. JPA 2.0 also adds support for pessimistic
locking and provides three lock modes for pessimistic locking. The two new optimistic lock modes are:
OPTIMISTIC
. This is the same as the READ
lock mode. The READ
lock mode isOPTIMISTIC
is recommended for new applications.OPTIMISTIC_FORCE_INCREMENT
. This is the same as the WRITE
lock mode. The WRITE
OPTIMISTIC_FORCE_INCREMENT
is recommended for newThe three new pessimistic lock modes are:
PESSIMISTIC_READ
. The entity manager locks the entity as soon as a transaction reads it. ThePESSIMISTIC_WRITE
. The entity manager locks the entity as soon as a transaction updates it.PESSIMISTIC_FORCE_INCREMENT
. The entity manager locks the entity when a transactionversion
attribute when the transaction ends,JPA 2.0 also provides multiple ways to specify the lock mode for an entity. You can specify the lock mode
in the lock()
and find()
methods of the EntityManager
. In addition,
if you call the EntityManager.refresh()
method, it refreshes the state of the entity instance
from the database and locks it based on the entity's lock mode.
You can also set the lock mode for a query through the setLockMode()
method of the Query
interface. And you can specify a lock mode for the results returned by a named query through thesetLockMode
element of the @NamedQuery
annotation.
Let's look at some examples of the new locking support in JPA 2.0.
OPTIMISTIC
Lock Mode
The typical use case for OPTIMISTIC
lock mode is where an entity has an intrinsic dependency on one or
more entities to ensure consistency, for example, when there is a relationship between two entities. In the example
shown in Figure 2, Transaction 1 on the left updates the price for part p1
. This
increments p1s
version
attribute. Transaction 2 on the right submits a bid for a user,u1
. If the part price is lower than the user's current bid, Transaction 2 increases the bid.
![]() |
Figure 2. Using OPTIMISTIC Lock Mode |
In this scenario, you don't want Transaction 2 to commit if Transaction T1 changes the price for the part
after Transaction T2 reads the price. So OPTIMISTIC
lock mode is a good choice:
Before committing Transaction 2, the entity manager checks the version
attribute for the p1
entity.
The p1
version
attribute is higher than when p1 was last read, so the entity manager throws anOptimisticLockException
and rolls back Transaction2. Note that checking u1s
version
attribute for an update would not throw an exception. That's because Transaction 1 updates p1s
version
attribute — it does not increment u1s
version
attribute.
OPTIMISTIC_FORCE_INCREMENT
Lock Mode
OPTIMISTIC_FORCE_INCREMENT
lock mode causes an optimistic lock failure if another transaction tries to
modify the locked entity. The common use for this lock is to guarantee consistency among entities in a relationship.
Figure 3 shows an example of OPTIMISTIC_FORCE_INCREMENT
lock mode.
![]() |
Figure 3. Using OPTIMISTIC_FORCE_INCREMENT Lock Mode |
Transaction 2 on the right wants to ensure that the price for a part p1
does not change during the transaction,
so it locks the p1
entity as follows:
Transaction 2 then calls em.flush()
— this increments p1s
version
attribute
in the database. Any parallel attempt to update p1
will throw an OptimisticLockException
and
roll back. As you can see, Transaction 1 attempts to update p1s price after Transaction 2 calls
em.flush()
. When Transaction T1 attempts to commit, the entity manager checks the p1
version
attribute. Because the attribute has been updated since the last read, the entity manager
throws an OptimisticLockException
and rolls back Transaction T1.
PESSIMISTIC
Lock Modes
The pessimistic lock modes lock a database row when data is read. This is the equivalent to the action
taken in response to the SQL statement SELECT . . . FOR UPDATE [NOWAIT]
. Pessimistic locking ensures
that transactions do not update the same entity at the same time. This can simplify application code, but it limits
concurrent access to the data, something that can cause poor scalability and may cause deadlocks. Pessimistic locking
is better for applications with a higher risk of contention among concurrent transactions.
The following figures show various examples of PESSIMISTIC
lock modes:
PESSIMISTIC_READ
lock mode.PESSIMISTIC_WRITE
lock mode.PESSIMISTIC_WRITE
lock mode.![]() |
Figure 4. Setting PESSIMISTIC_READ Lock Mode After Reading an Entity |
![]() |
Figure 5. Setting PESSIMISTIC_WRITE Lock Mode While Reading an Entity |
![]() |
Figure 6. Setting PESSIMISTIC_WRITE Lock Mode After Reading an Entity |
The right locking approach to use depends on your application. Some questions you might want to ask to help make the decision
are:
Sample Application
Accompanying this tip is a sample application that demonstrates some of the locking support
in JPA 2.0. The application is also available in the Java EE 6
SDK Preview release — look for "The Java Persistence API Locking Sample Application" in thesamples
directory of the Java EE 6 SDK Preview release download package.
The application consists of a client, a servlet, entity classes for part and user data, and stateless session beans that
provide the logic for accessing and updating the data. The client calls the servlet to initialize the data. The client then
makes multiple requests to the servlet that simulate parallel read and update operations. These operations are performed
by the beans. Some of the operations are performed using optimistic locking, some using pessimistic locking. For example,
the following method, updateWithOptimisticReadLock()
demonstrates parallel operations performed using
optimistic locking.
The updateWithOptimisticReadLock()
method calls the updatePrice()
method in the partEJB
bean to find a user and then update the price of a part. The updateWithOptimisticReadLock()
method then waits to
allow parallel method calls to find other users before calling the updateBid()
method in the userEJB
bean. The updateBid()
method sets an optimistic lock for the part and then submits a user bid that is based on the
part price, as shown below:
The updateWithOptimisticReadLock()
method then calls em.flush()
. At that point, the entity
manager performs a version check on the part entity. If any transaction submitted by any of the other users updates the part
while it is locked, the entity manager increments the part's version attribute. If the version attribute for the part
is higher than it was when the part was last read by the set bid transaction, the updateWithOptimisticReadLock()
method throws an OptimisticLockException
and rolls back that transaction.
You can find the source code for the application in the /samples/javaee6/jpa/locking
directory.
To run the sample application, do the following:
/javaee6/jpa/locking
directory, whereYou can replace the ant all
command with the
following set of commands:
ant default
— compiles and packages the application.
ant dir
— deploys the application to the application server.
ant run
— runs the test Java client.
In response, you should see output similar to the following:
The operations, which are identified in the tc
parameter values in the URL calls, are as follows:
updateWOL
. Finds a part. Simulates think time to allow parallel threads to find users in parallel.updateWOR
. Finds a part and a user. Simulates think time to allow parallel threads to find users in parallel.updateWOW
. Finds a part and a user. Simulates think time to allow parallel threads to find users in parallel.updateWRP
. Finds a part. Simulates think time to allow parallel threads to find users in parallel.updateWRR
. Finds a part. Simulates think time to allow parallel threads to find users in parallel.updateWPL
. Finds a part using a PESSIMISTIC_WRITE lock. Simulates think time to allow parallel threads to findNotice that some update operations that use optimistic locking, such as updateWOL
, fail, while all update
operations that use pessimistic locking, such as updateWPL
, are successful. However, the time it takes to update
using pessimistic locking is much higher than that taken using optimistic locking.
Use the command ant clean
to undeploy the sample application and to remove temporary directories.
Further Reading
For more information, see the following resources:
About the Author
Carol McDonald is a Java Technology Evangelist at Sun Microsystems. As a software developer since 1986, Carol's experience
has been in the technology areas of distributed network applications and protocols, including Java EE technology, XML,
Internet/Intranet applications, LDAP, Distributed Network Management (CMIP,SNMP) and Email (X.400,X.500). Besides Java,
Carol is also fluent in French and German. Read Carol McDonald's
blog.
Nice to see that JPA 2 gets additional features for concurrency control. But what I'm completely missing in your story is that concurrency control is very database implementation specific and that not understanding the concurrency schema used, leads to a false sense of security.
Imho abstracting too much away from the database leads to all kinds of problems (performance / correctness) but also more subtle liveness problems like livelocking and starvation.
So having a comprehensive API still doesn't fix problems. You really need to know the database to see what is going on under the hood (and you also need to check the generated sql to see if it is what you would expect).
O/R Frameworks significantly reduce the code and development time for programmers. However this does not mean you can ignore what's going on with the database. For example with MySQL locking maybe by row with MVCC or by table depending on which storage engine you use. I wrote 2 blog entries on this: JPA Performance, Don't Ignore the Database http://blogs.sun.com/carolmcdonald/entry/jpa_performance_don_t_ignore and MySQL for Developers http://blogs.sun.com/carolmcdonald/entry/mysql_for_developers
Hi,
I had difficulties for using the locking features before.Now I think after reading this article now I can use the locking features more efficiently.
Can these types of locking be effectively used in a container managed transaction context, if the Session bean is running with CMT and it is using JPA and database ?