blob: 3db9ae6fd9f44059ae30b410f5dc9314ce817cb3 [file] [log] [blame]
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>3.&nbsp; Object Locking</title><link rel="stylesheet" href="css/docbook.css" type="text/css"><base href="display"><meta name="generator" content="DocBook XSL Stylesheets V1.72.0"><link rel="start" href="manual.html" title="Apache OpenJPA 2.0 User's Guide"><link rel="up" href="ref_guide_runtime.html" title="Chapter&nbsp;9.&nbsp; Runtime Extensions"><link rel="prev" href="ref_guide_runtime_jpa.html" title="2.&nbsp; JPA Extensions"><link rel="next" href="ref_guide_savepoints.html" title="4.&nbsp; Savepoints"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.&nbsp;
Object Locking
</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ref_guide_runtime_jpa.html">Prev</a>&nbsp;</td><th width="60%" align="center">Chapter&nbsp;9.&nbsp;
Runtime Extensions
</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="ref_guide_savepoints.html">Next</a></td></tr></table><hr></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="ref_guide_locking"></a>3.&nbsp;
Object Locking
</h2></div></div></div><div class="toc"><dl><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_default">3.1.
Configuring Default Locking
</a></span></dt><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_runtime">3.2.
Configuring Lock Levels at Runtime
</a></span></dt><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_apis">3.3.
Object Locking APIs
</a></span></dt><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_lockmgr">3.4.
Lock Manager
</a></span></dt><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_rules">3.5.
Rules for Locking Behavior
</a></span></dt><dt><span class="section"><a href="ref_guide_locking.html#ref_guide_locking_issues">3.6.
Known Issues and Limitations
</a></span></dt></dl></div><a class="indexterm" name="d0e31972"></a><p>
Controlling how and when objects are locked is an important part of maximizing
the performance of your application under load. This section describes OpenJPA's
APIs for explicit locking, as well as its rules for implicit locking.
</p><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_default"></a>3.1.&nbsp;
Configuring Default Locking
</h3></div></div></div><a class="indexterm" name="d0e31980"></a><p>
<a class="indexterm" name="d0e31987"></a>
<a class="indexterm" name="d0e31993"></a>
<a class="indexterm" name="d0e31997"></a>
You can control OpenJPA's default transactional read and write lock levels
through the <a href="ref_guide_conf_openjpa.html#openjpa.ReadLockLevel" title="5.56.&nbsp; openjpa.ReadLockLevel"><code class="literal">
openjpa.ReadLockLevel</code></a> and
<a href="ref_guide_conf_openjpa.html#openjpa.WriteLockLevel" title="5.67.&nbsp; openjpa.WriteLockLevel"><code class="literal">openjpa.WriteLockLevel</code>
</a> configuration properties. Each property accepts a value of <code class="literal">
none</code>, <code class="literal">read</code>, <code class="literal">write</code>,
<code class="literal">optimistic</code>, <code class="literal">optimistic-force-increment</code>,
<code class="literal">pessimistic-read</code>, <code class="literal">pessimistic-write</code>,
<code class="literal">pessimistic-force-increment</code>, or a number
corresponding to a lock level defined by the
<a href="ref_guide_locking.html#ref_guide_locking_lockmgr" title="3.4.&nbsp; Lock Manager">lock manager</a> in use. These
properties apply only to non-optimistic transactions; during optimistic
transactions, OpenJPA never locks objects by default.
</p><p>
<a class="indexterm" name="d0e32039"></a>
<a class="indexterm" name="d0e32043"></a>
You can control the default amount of time OpenJPA will wait when trying to
obtain locks through the <a href="ref_guide_conf_openjpa.html#openjpa.LockTimeout" title="5.41.&nbsp; openjpa.LockTimeout"><code class="literal">
openjpa.LockTimeout</code></a> configuration property. Set this property
to the number of milliseconds you are willing to wait for a lock before OpenJPA
will throw an exception, or to -1 for no limit. It defaults to -1.
</p><div class="example"><a name="ref_guide_locking_default_conf"></a><p class="title"><b>Example&nbsp;9.3.&nbsp;
Setting Default Lock Levels
</b></p><div class="example-contents"><pre class="programlisting">
&lt;property name="openjpa.ReadLockLevel" value="none"/&gt;
&lt;property name="openjpa.WriteLockLevel" value="write"/&gt;
&lt;property name="openjpa.LockTimeout" value="30000"/&gt;
</pre></div></div><br class="example-break"></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_runtime"></a>3.2.&nbsp;
Configuring Lock Levels at Runtime
</h3></div></div></div><a class="indexterm" name="d0e32061"></a><p>
At runtime, you can override the default lock levels through the <code class="classname">
FetchPlan</code> interface described above. At the beginning of each
datastore transaction, OpenJPA initializes the <code class="classname"> EntityManager
</code>'s fetch plan with the default lock levels and timeouts described
in the previous section. By changing the fetch plan's locking properties, you
can control how objects loaded at different points in the transaction are
locked. You can also use the fetch plan of an individual <code class="classname">Query
</code> to apply your locking changes only to objects loaded through that
<code class="classname">Query</code>.
</p><pre class="programlisting">
public LockModeType getReadLockMode();
public FetchPlan setReadLockMode(LockModeType mode);
public LockModeType getWriteLockMode();
public FetchPlan setWriteLockMode(LockModeType mode);
long getLockTimeout();
FetchPlan setLockTimeout(long timeout);
</pre><p>
Controlling locking through these runtime APIs works even during optimistic
transactions. At the end of the transaction, OpenJPA resets the fetch plan's
lock levels to <code class="literal">none</code>. You cannot lock objects outside of a
transaction.
</p><div class="example"><a name="ref_guide_locking_fetch"></a><p class="title"><b>Example&nbsp;9.4.&nbsp;
Setting Runtime Lock Levels
</b></p><div class="example-contents"><pre class="programlisting">
import org.apache.openjpa.persistence.*;
...
EntityManager em = ...;
em.getTransaction().begin();
// load stock we know we're going to update at write lock mode
Query q = em.createQuery("select s from Stock s where symbol = :s");
q.setParameter("s", symbol);
OpenJPAQuery oq = OpenJPAPersistence.cast(q);
FetchPlan fetch = oq.getFetchPlan();
fetch.setReadLockMode(LockModeType.WRITE);
fetch.setLockTimeout(3000); // 3 seconds
Stock stock = (Stock) q.getSingleResult();
// load an object we don't need locked at none lock mode
fetch = OpenJPAPersistence.cast(em).getFetchPlan();
fetch.setReadLockMode(null);
Market market = em.find(Market.class, marketId);
stock.setPrice(market.calculatePrice(stock));
em.getTransaction().commit();
</pre></div></div><br class="example-break"></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_apis"></a>3.3.&nbsp;
Object Locking APIs
</h3></div></div></div><a class="indexterm" name="d0e32095"></a><p>
In addition to allowing you to control implicit locking levels, OpenJPA provides
explicit APIs to lock objects and to retrieve their current lock level.
</p><pre class="programlisting">
public LockModeType OpenJPAEntityManager.getLockMode(Object pc);
</pre><p>
Returns the level at which the given object is currently locked.
</p><p>
In addition to the standard
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="http://java.sun.com/javaee/6/docs/api/javax/persistence/EntityManager.html" target="_top">
<code class="methodname">EntityManager.lock(Object, LockModeType)</code></a>
method, the
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/persistence/OpenJPAEntityManager.html" target="_top">
<code class="classname">OpenJPAEntityManager</code></a> exposes the following
methods to lock objects explicitly:
</p><pre class="programlisting">
public void lock(Object pc);
public void lock(Object pc, LockModeType mode, long timeout);
public void lockAll(Object... pcs);
public void lockAll(Object... pcs, LockModeType mode, long timeout);
public void lockAll(Collection pcs);
public void lockAll(Collection pcs, LockModeType mode, long timeout);
</pre><p>
Methods that do not take a lock level or timeout parameter default to the
current fetch plan. The example below demonstrates these methods in action.
</p><div class="example"><a name="ref_guide_locking_explicit"></a><p class="title"><b>Example&nbsp;9.5.&nbsp;
Locking APIs
</b></p><div class="example-contents"><pre class="programlisting">
import org.apache.openjpa.persistence.*;
// retrieve the lock level of an object
OpenJPAEntityManager oem = OpenJPAPersistence.cast(em);
Stock stock = ...;
LockModeType level = oem.getLockMode(stock);
if (level == OpenJPAModeType.WRITE) ...
...
oem.setOptimistic(true);
oem.getTransaction().begin();
// override default of not locking during an opt trans to lock stock object
oem.lock(stock, LockModeType.WRITE, 1000);
stock.setPrice(market.calculatePrice(stock));
oem.getTransaction().commit();
</pre></div></div><br class="example-break"></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_lockmgr"></a>3.4.&nbsp;
Lock Manager
</h3></div></div></div><a class="indexterm" name="d0e32130"></a><p>
<a class="indexterm" name="d0e32137"></a>
OpenJPA delegates the actual work of locking objects to the system's
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/kernel/LockManager.html" target="_top"><code class="classname">
org.apache.openjpa.kernel.LockManager</code></a>. This plugin is
controlled by the <a href="ref_guide_conf_openjpa.html#openjpa.LockManager" title="5.40.&nbsp; openjpa.LockManager"><code class="literal">
openjpa.LockManager</code></a> configuration property. You can write your
own lock manager, or use one of the bundled options:
</p><div class="itemizedlist"><ul type="disc"><li><p>
<code class="literal">mixed</code>: This is an alias for the
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/jdbc/kernel/MixedLockManager.html" target="_top">
<code class="classname">org.apache.openjpa.jdbc.kernel.MixedLockManager</code>
</a>, which implements the JPA 2.0 specification entity locking behaviors.
It combines both the optimistic and pessimistic semantics controlled by
lock mode argument in methods define in the EntityManager
and Query interfaces or OpenJPA lock level properties.
</p><p>
The <code class="literal">mixed</code> LockManager inherits all the properties available
from <code class="literal">version</code> and <code class="literal">pessimistic</code> LockManagers.
For example: <code class="literal">VersionCheckOnReadLock</code> and
<code class="literal">VersionUpdateOnWriteLock</code> properties.
</p><p>
This is the default <code class="literal">openjpa.LockManager</code> setting in OpenJPA.
</p></li><li><p>
<code class="literal">pessimistic</code>: This is an alias for the
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.html" target="_top">
<code class="classname">org.apache.openjpa.jdbc.kernel.PessimisticLockManager</code>
</a>, which
uses SELECT FOR UPDATE statements (or the database's equivalent) to lock the
database rows corresponding to locked objects. This lock
manager does not distinguish between read locks and write locks; all locks are
write locks.
</p><p>
The <code class="literal">pessimistic</code> LockManager can be configured to additionally
perform the version checking and incrementing behavior of the <code class="literal">version
</code> lock manager described below by setting its <code class="literal">
VersionCheckOnReadLock</code> and <code class="literal">VersionUpdateOnWriteLock</code>
properties:
</p><pre class="programlisting">
&lt;property name="openjpa.LockManager" value="pessimistic(VersionCheckOnReadLock=true,VersionUpdateOnWriteLock=true)"/&gt;
</pre></li><li><p>
<code class="literal">version</code>: This is an alias for the
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/kernel/VersionLockManager.html" target="_top">
<code class="classname">org.apache.openjpa.kernel.VersionLockManager</code></a>.
This lock manager does not perform any exclusive locking, but instead ensures
read consistency by verifying that the version of all read-locked instances is
unchanged at the end of the transaction. Furthermore, a write lock will force an
increment to the version at the end of the transaction, even if the object is
not otherwise modified. This ensures read consistency with non-blocking
behavior.
</p></li><li><p>
<code class="literal">none</code>: This is an alias for the
<a xmlns:xlink="http://www.w3.org/1999/xlink" href="../javadoc/org/apache/openjpa/kernel/NoneLockManager.html" target="_top">
<code class="classname">org.apache.openjpa.kernel.NoneLockManager</code></a>, which
does not perform any locking at all.
</p></li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
In order for the <code class="literal">version</code> or <code class="literal">mixed</code> lock
managers to prevent the dirty
read phenomenon, the underlying data store's transaction isolation level must be
set to the equivalent of "read committed" or higher.
</p></div><div class="example"><a name="ref_guide_locking_disable"></a><p class="title"><b>Example&nbsp;9.6.&nbsp;
Disabling Locking
</b></p><div class="example-contents"><pre class="programlisting">
&lt;property name="openjpa.LockManager" value="none"/&gt;
</pre></div></div><br class="example-break"></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_rules"></a>3.5.&nbsp;
Rules for Locking Behavior
</h3></div></div></div><a class="indexterm" name="d0e32251"></a><a class="indexterm" name="d0e32256"></a><p>
Advanced persistence concepts like lazy-loading and object uniquing create
several locking corner-cases. The rules below outline OpenJPA's implicit locking
behavior in these cases.
</p><div class="orderedlist"><ol type="1"><li><p>
When an object's state is first read within a transaction, the object is locked
at the fetch plan's current read lock level. Future reads of additional lazy
state for the object will use the same read lock level, even if the fetch plan's
level has changed.
</p></li><li><p>
When an object's state is first modified within a transaction, the object is
locked at the write lock level in effect when the object was first read, even if
the fetch plan's level has changed. If the object was not read previously, the
current write lock level is used.
</p></li><li><p>
When objects are accessed through a persistent relation field, the related
objects are loaded with the fetch plan's current lock levels, not the lock
levels of the object owning the field.
</p></li><li><p>
Whenever an object is accessed within a transaction, the object is re-locked at
the current read lock level. The current read and write lock levels become those
that the object "remembers" according to rules one and two above.
</p></li><li><p>
If you lock an object explicitly through the APIs demonstrated above, it is
re-locked at the specified level. This level also becomes both the read and
write level that the object "remembers" according to rules one and two above.
</p></li><li><p>
When an object is already locked at a given lock level, re-locking at a lower
level has no effect. Locks cannot be downgraded during a transaction.
</p></li></ol></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="ref_guide_locking_issues"></a>3.6.&nbsp;
Known Issues and Limitations
</h3></div></div></div><a class="indexterm" name="d0e32285"></a><p>
Due to performance concerns and database limitations, locking cannot be perfect.
You should be aware of the issues outlined in this section, as they may affect
your application.
</p><div class="itemizedlist"><ul type="disc"><li><p>
Typically, during optimistic transactions OpenJPA does not start an actual
database transaction until you flush or the optimistic transaction commits. This
allows for very long-lived transactions without consuming database resources.
When using the pessimistic lock manager, however, OpenJPA must begin a database
transaction whenever you decide to lock an object during an optimistic
transaction. This is because the pessimistic lock manager uses database locks,
and databases cannot lock rows without a transaction in progress. OpenJPA will
log an INFO message to the <code class="literal">openjpa.Runtime</code> logging channel
when it begins a datastore transaction just to lock an object.
</p></li><li><p>
In order to maintain reasonable performance levels when loading object state,
OpenJPA can only guarantee that an object is locked at the proper lock level
<span class="emphasis"><em>after</em></span> the state has been retrieved from the database. This
means that it is technically possible for another transaction to "sneak in" and
modify the database record after OpenJPA retrieves the state, but before it
locks the object. The only way to positively guarantee that the object is locked
and has the most recent state to refresh the object after locking it.
</p><p>
When using the pessimistic lock manager, the case above can only occur when
OpenJPA cannot issue the state-loading SELECT as a locking statement due to
database limitations. For example, some databases cannot lock SELECTs that use
joins. The pessimistic lock manager will log an INFO message to the <code class="literal">
openjpa.Runtime</code> logging channel whenever it cannot lock the initial
SELECT due to database limitations. By paying attention to these log messages,
you can see where you might consider using an object refresh to guarantee that
you have the most recent state, or where you might rethink the way you load the
state in question to circumvent the database limitations that prevent OpenJPA
from issuing a locking SELECT in the first place.
</p></li></ul></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ref_guide_runtime_jpa.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="ref_guide_runtime.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="ref_guide_savepoints.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">2.&nbsp;
JPA Extensions
&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="manual.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;4.&nbsp;
Savepoints
</td></tr></table></div></body></html>