Kodo utilizes several configurable caches to maximize performance. This chapter explores Kodo's data cache, query cache, and query compilation cache.
The Kodo data cache is an optional cache of persistent object data that
operates at the EntityManagerFactory
/
PersistenceManagerFactory
level. This cache is
designed to significantly increase performance while remaining in full
compliance with the JPA and JDO persistence standards. This means that
turning on the
caching option can transparently increase the performance of your
application, with no changes to your code.
Important | |
---|---|
This feature requires Kodo Enterprise Edition or Kodo Professional Edition. |
Kodo's data cache is not related to the EntityManager
or PersistenceManager
caches dictated by the
JPA and JDO persistence specifications. These specifications
mandate behavior for the EntityManager
and
PersistenceManager
caches aimed at guaranteeing
transaction isolation when operating on persistent objects.
Kodo's data cache is designed to provide significant performance increases over cacheless operation, while guaranteeing that behavior will be identical in both cache-enabled and cacheless operation.
There are five ways to access data via the Kodo APIs:
standard relation traversal, large result set relation traversal,
queries, looking up an object by id, and iteration over an
Extent
. Kodo's cache plugin accelerates three of
these mechanisms. It does not provide any caching of large result set
relations or Extent
iterators. If you find yourself
in need of higher-performance Extent
iteration, see
Example 10.22, “Query Replaces Extent”.
Table 10.1. Data access methods
Access method | Uses cache |
---|---|
Standard relation traversal | Yes |
Large result set relation traversal | No |
Query | Yes |
Lookups by object id | Yes |
Iteration over an Extent
| No |
When enabled, the cache is checked before making a trip to the datastore. Data is stored in the cache when objects are committed and when persistent objects are loaded from the datastore.
Kodo's data cache can in both single-JVM and multi-JVM environments. Multi-JVM caching is achieved through the use of the distributed event notification framework described in Section 11.3, “Remote Event Notification Framework”, or through one of Kodo's integrations with third-party distributed caches (see Section 10.1.4, “Third-Party Integrations”).
The single JVM mode of operation maintains and shares a data
cache across all EntityManager
or
PersistenceManager
instances obtained from a particular
EntityManagerFactory
or
PersistenceManagerFactory
. This is not appropriate for use in a
distributed environment, as caches in different JVMs or created from
different factory objects will not be synchronized.
Note | |
---|---|
If you mix and match |
To enable the basic single-factory cache set the
kodo.DataCache
property to true
, and set the
kodo.RemoteCommitProvider
property to
sjvm
:
Example 10.1. Single-JVM Data Cache
JPA XML format:
<property name="kodo.DataCache" value="true"/> <property name="kodo.RemoteCommitProvider" value="sjvm"/>
JDO properties format:
kodo.DataCache: true kodo.RemoteCommitProvider: sjvm
To configure the data cache to remain up-to-date in a distributed
environment, set the
kodo.RemoteCommitProvider
property appropriately, or
integrate Kodo with a third-party caching solution.
Remote commit providers are described in
Section 11.3, “Remote Event Notification Framework”.
Section 10.1.4, “Third-Party Integrations” enumerates supported
third-party caching solutions.
Kodo's default implementation maintains a concurrent map of object ids
to cache data. By default, 1000 elements are kept in cache. This can
be adjusted by setting the CacheSize
property in
your plugin string - see below for an example. Objects that are
pinned into the cache are not counted when determining if the cache
size exceeds the maximum.
When the maximum cache size is exceeded, random entries are
are moved to a soft reference map, so they may stick
around for a little while longer. You can control
the number of soft references Kodo keeps with the
SoftReferenceSize
property. Soft references are unlimited by
default. Set to 0 to disable soft references completely.
Example 10.2. Data Cache Size
JPA XML format:
<property name="kodo.DataCache" value="true(CacheSize=5000, SoftReferenceSize=0)"/>
JDO properties format:
kodo.DataCache: true(CacheSize=5000, SoftReferenceSize=0)
Kodo offers a least-recently-used (LRU) caching option in addition to
the default concurrent cache. Substitute lru
for
true
in your kodo.DataCache
setting to utilize the LRU cache. Note that while the LRU cache's
eviction scheme is more optimal than the default cache's random
evictions, it also requires much more synchronization. Generally, the
default cache's higher concurrency results in better performance than
the LRU cache's smarter eviction scheme.
Note | |
---|---|
The default concurrent cache does not fully index its contents by class. Rather, it tracks which clasess are in the cache. It services requests to drop given classes by checking to see if any instances of that class might be in the cache, and then clearing the entire cache. You can work around this inefficiency with careful cache partitioning. The LRU cache, however, does not suffer from this limitation. Dropping a class from the LRU cache only drops entries for that class. |
Example 10.3. LRU Cache
The LRU cache has the same sizing properties as the default cache.
JPA XML format:
<property name="kodo.DataCache" value="lru(CacheSize=5000, SoftReferenceSize=0)"/>
JDO properties format:
kodo.DataCache: lru(CacheSize=5000, SoftReferenceSize=0)
You can specify a cache timeout value for a class by setting the timeout metadata extension to the amount of time in milliseconds a class's data is valid. Use a value of -1 for no expiration. This is the default value.
Example 10.4. Data Cache Timeout
Timeout Employee
objects after 10 seconds.
JPA:
@Entity @DataCache(timeout=10000) public class Employee { ... }
JDO:
<class name="Employee"> <extension vendor-name="kodo" key="data-cache-timeout" value="10000"/> ... </class>
See the
org.apache.openjpa.persistence.DataCache
Javadoc
for more information on the DataCache
annotation.
A cache can specify that it should be cleared at certain times
rather than using data timeouts. The
EvictionSchedule
property of Kodo's
cache implementation accepts a cron
style eviction schedule. The format of this property is a
whitespace-separated list of five tokens, where the
*
symbol (asterisk), indicates match all. The
tokens are, in order:
Minute
Hour of Day
Day of Month
Month
Day of Week
For example, the following kodo.DataCache
setting
schedules the default cache to evict values
from the cache at 15 and 45 minutes past 3 PM on Sunday.
true(EvictionSchedule='15,45 15 * * 1')
It is also possible for different persistence-capable classes to use different caches. This is achieved by specifying a cache name in a metadata extension.
Example 10.5. Named Data Cache Specification
JPA:
import org.apache.openjpa.persistence.*; @Entity @DataCache(name="small-cache", timeout=10000) public class Employee { ... }
JDO:
<class name="Employee"> <extension vendor-name="kodo" key="data-cache" value="small-cache"/> <extension vendor-name="kodo" key="data-cache-timeout" value="10000"/> ... </class>
See the
org.apache.openjpa.persistence.DataCache
Javadoc
for more information on the DataCache
annotation.
The metadata above will cause instances of the Employee
class to be stored in a cache named
small-cache
.
This small-cache
cache can be explicitly configured
in the kodo.DataCache
plugin string, or can be
implicitly defined, in which case it will take on the same
default configuration properties as the default cache identified
in the kodo.DataCache
property.
The kodo.datacache
package defines Kodo's
data caching framework. While you may use this framework directly
(see its
Javadoc for details), its APIs are meant primarily for
service providers. In fact, Section 10.1.5, “Cache Extension”
below has tips on how to use this package to extend Kodo's caching
service yourself.
Rather than use the low-level kodo.datacache
package APIs, JPA users should typically access the data
cache through Kodo's high-level
org.apache.openjpa.persistence.StoreCache
facade.
This facade has methods to pin and unpin records, evict data from the
cache, and more.
public StoreCache getStoreCache (); public StoreCache getStoreCache (String name);
You obtain the StoreCache
through the
OpenJPAEntityManagerFactory.getStoreCache
methods.
When you have multiple data caches configured as in the
small-cache
example above, the
StoreCache
can act as a unified facade over all your
caches. For every oid parameter to the StoreCache
methods, it determines the correct data cache for that
oid's corresponding persistent class, and dynamically delegates to
that cache.
If you know that you want to access a certain data cache and no others,
the
OpenJPAEntityManagerFactory.getStoreCache(String name)
method returns a StoreCache
interface to a particular named data cache.
Example 10.7. Accessing the StoreCache
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf); StoreCache cache = oemf.getStoreCache (); ... StoreCache smallCache = oemf.getStoreCache ("small-cache"); ...
public void evict (Class cls, Object oid); public void evictAll (); public void evictAll (Class cls, Object... oids); public void evictAll (Class cls, Collection oids);
The evict
methods tell the cache to release
data. Each method takes an entity class and one or more identity
values, and releases the cached data for the corresponding persistent
instances. The evictAll
method with no
arguments clears the cache. Eviction is useful when the datastore is
changed by a separate process outside Kodo's control. In this
scenario, you typically
have to manually evict the data from the datastore cache; otherwise the
Kodo runtime, oblivious to the changes, will maintain its stale copy.
public void pin (Class cls, Object oid); public void pinAll (Class cls, Object... oids); public void pinAll (Class cls, Collection oids); public void unpin (Class cls, Object oid); public void unpinAll (Class cls, Object... oids); public void unpinAll (Class cls, Collection oids);
Most caches are of limited size. Pinning an identity to the cache
ensures that the cache will mill not kick the data for the corresponding
instance out of the cache, unless you manually evict it. Note that even
after manual eviction, the data will get pinned again the next time it
is fetched from the store. You can only remove a pin and make the data
once again available for normal cache overflow eviction through the
unpin
methods. Use pinning when you want a
guarantee that a certain object will always be available from cache,
rather than requiring a datastore trip.
Example 10.8. StoreCache Usage
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf); StoreCache cache = oemf.getStoreCache (); cache.pin (Magazine.class, popularMag.getId ()); cache.evict (Magazine.class, changedMag.getId ());
See the StoreCache
Javadoc
for information on additional functionality it provides.
Also, Chapter 9, Runtime Extensions discusses Kodo's other
extensions to the standard set of JPA runtime interfaces.
JDO users can access the data cache through the JDO-standard
javax.jdo.datastore.DataStoreCache
facade. The JDO Overview describes this interface in
Chapter 13, DataStoreCache.
Kodo extends the standard JDO cache in two ways:
The
kodo.jdo.KodoDataStoreCache
interface extends JDO's DataStoreCache
with many useful methods.
When you have multiple data caches configured as in
the small-cache
example above, Kodo's
DataStoreCache
implementation acts as
a facade over all your data caches. For every oid parameter
passed to the DataStoreCache
methods, it
determines the correct data cache for that oid's corresponding
persistent class, and dynamically delegates to that cache.
If you know that you want to access a certain data cache and
no others, the
KodoPersistenceManagerFactory.getDataStoreCache(String name)
method returns a JDO
DataStoreCache
interface to a particular named
data cache.
Example 10.9. Accessing a Named Cache
import kodo.jdo.*; ... KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf); DataStoreCache cache = kpmf.getDataStoreCache ("small-cache"); cache.evict (JDOHelper.getObjectId (changedObj)); cache.pin (JDOHelper.getObjectId (popularObj));
You can read more about Kodo extensions to JDO APIs in Chapter 9, Runtime Extensions.
The examples above include calls to evict
to
manually remove data from the data cache. Rather than evicting objects
from the data cache directly, you can also configure Kodo to
automatically evict objects from the data cache when
you use the OpenJPAEntityManager
or
PersistenceManager
's eviction APIs.
Example 10.10. Automatic Data Cache Eviction
JPA XML format:
<property name="kodo.BrokerImpl" value="EvictFromDataCache=true"/>
JDO properties format:
kodo.BrokerImpl: EvictFromDataCache=true
JPA:
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManager oem = OpenJPAPersistence.cast (em); oem.evict (changedMag); // will evict from data cache also
JDO:
PersistenceManager pm = ...; pm.evict (changedMag); // will evict from data cache also
In addition to the data cache, the kodo.datacache
package defines service provider interfaces for a query cache.
The query cache is enabled by default when the data cache
is enabled. The query cache stores the object ids returned by
query executions. When you run a query, Kodo assembles a key based on
the query properties and the parameters used at execution time,
and checks for a cached query result. If one is found, the
object ids in the cached result are looked up, and the resultant
persistence-capable objects are returned. Otherwise, the query
is executed against the database, and the object ids loaded by
the query are put into the cache. The object id list is not
cached until the list returned at query execution time is fully
traversed.
Kodo JPA exposes a high-level interface to the query cache through
the
org.apache.openjpa.persistence.QueryResultCache
class.
You can access this class through the
OpenJPAEntityManagerFactory
.
Kodo JDO exposes a high-level interface to the query cache through
the
kodo.jdo.QueryResultCache
class. You can access
this class through the
KodoPersistenceManagerFactory
.
Example 10.11. Accessing the QueryResultCache
JPA:
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf); QueryResultCache qcache = oemf.getQueryResultCache ();
JDO:
import kodo.jdo.*; ... KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf); QueryResultCache qcache = kpmf.getQueryResultCache ();
The default query cache implementation caches 100 query
executions in a concurrent map. This can be changed
by setting the cache size in the CacheSize
plugin property. Like the data cache, the query cache also has a
backing soft reference map. The SoftReferenceSize
property controls the size of this map. It is disabled by default.
Example 10.12. Query Cache Size
JPA XML format:
<property name="kodo.QueryCache" value="CacheSize=1000, SoftReferenceSize=100"/>
JDO properties format:
kodo.QueryCache: CacheSize=1000, SoftReferenceSize=100
Just as Kodo offers an LRU data caching option, Kodo includes an LRU query cache as well. Once again, it has the same sizing options as the default query cache.
Example 10.13. LRU Query Cache
JPA XML format:
<property name="kodo.QueryCache" value="lru(CacheSize=1000, SoftReferenceSize=100)"/>
JDO properties format:
kodo.QueryCache: lru(CacheSize=1000, SoftReferenceSize=100)
To disable the query cache completely, set the
kodo.QueryCache
property to false
:
Example 10.14. Disabling the Query Cache
JPA XML format:
<property name="kodo.QueryCache" value="false"/>
JDO properties format:
kodo.QueryCache: false
There are certain situations in which the query cache is bypassed:
Caching is not used for in-memory queries (queries in which
the candidates are a collection instead of a class or
Extent
).
Caching is not used in transactions that have
IgnoreChanges
set to false
and in which modifications to classes in the query's access
path have occurred. If none of the classes in the access path
have been touched, then cached results are still valid and
are used.
Caching is not used in pessimistic transactions, since Kodo must go to the database to lock the appropriate rows.
Caching is not used when the the data cache does not have any cached data for an id in a query result.
Queries that use persistence-capable objects as parameters are only cached if the parameter is directly compared to field, as in:
JPQL:
select e from Employee e where e.company.address = :addr
JDOQL:
select from com.xyz.Employee where company.address == :addr
If you extract field values from the parameter in your query string, or if the parameter is used in collection element comparisons, the query is not cached.
Queries that result in projections of custom field types or
BigDecimal
or
BigInteger
fields are not cached.
Cache results are removed from the cache when instances of
classes in a cached query's access path are touched. That is, if
a query accesses data in class A
, and instances
of class A
are modified, deleted, or inserted,
then the cached query result is dropped from the cache.
It is possible to tell the query cache that a class has been altered. This is only necessary when the changes occur via direct modification of the database outside of Kodo's control. You can also evict individual queries, or clear the entire cache.
JPA:
public void evict (Query q); public void evictAll (Class cls); public void evictAll ();
JDO:
public void evict (Query q); public void evict (Query q, Object[] params); public void evict (Query q, Map params); public void evictAll (Class cls); public void evictAll ();
For JPA queries with parameters, set the desired parameter values
into the
Query
instance before calling the
above methods.
For JDO queries with parameters, pass the parameter values of the
query in the params
array or map.
Example 10.15. Evicting Queries
JPA:
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf); QueryResultCache qcache = oemf.getQueryResultCache (); // evict all queries that can be affected by changes to Magazines qcache.evictAll (Magazine.class); // evict an individual query with parameters EntityManager em = emf.createEntityManager (); Query q = em.createQuery (...). setParameter (0, paramVal0). setParameter (1, paramVal1); qcache.evict (q);
JDO:
import kodo.jdo.*; ... KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf); QueryResultCache qcache = kpmf.getQueryResultCache (); // evict all queries that can be affected by changes to Magazines qcache.evictAll (Magazine.class); // evict an individual query with parameters PersistenceManager pm = pmf.getPersistenceManager (); Query q = pm.newQuery (...); qcache.evictAll (q, new Object[] { paramVal0, paramVal1 });
When using one of Kodo's distributed cache implementations, it is necessary to perform this in every JVM - the change notification is not propagated automatically. When using a coherent cache implementation such as Kodo's Tangosol cache implementation, it is not necessary to do this in every JVM (although it won't hurt to do so), as the cache results are stored directly in the coherent cache.
Queries can also be pinned and unpinned
through the QueryResultCache
. The semantics of
these operations are the same as pinning and unpinning data from the
data cache.
JPA:
public void pin (Query q); public void unpin (Query q);
JDO:
public void pin (Query q); public void pin (Query q, Object[] params); public void pin (Query q, Map params); public void unpin (Query q); public void unpin (Query q, Object[] params); public void unpin (Query q, Map params);
For JPA queries with parameters, set the desired parameter values
into the
Query
instance before calling the
above methods.
For JDO queries with parameters, pass the parameter values of the
query in the params
array or map.
The following example shows these APIs in action.
Example 10.16. Pinning, and Unpinning Query Results
JPA:
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf); QueryResultCache qcache = oemf.getQueryResultCache (); EntityManager em = emf.createEntityManager (); Query pinQuery = em.createQuery (...). setParameter (0, paramVal0). setParameter (1, paramVal1); qcache.pin (pinQuery); Query unpinQuery = em.createQuery (...). setParameter (0, paramVal0). setParameter (1, paramVal1); qcache.unpin (unpinQuery);
JDO:
import kodo.jdo.*; ... KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf); QueryResultCache qcache = kpmf.getQueryResultCache (); PersistenceManager pm = pmf.getPersistenceManager (); Query pinQuery = pm.newQuery (...); qcache.pin (pinQuery, new Object[] { paramVal0, paramVal1 }); Query unpinQuery = pm.newQuery (...); qcache.unpin (unpinQuery, new Object[] { paramVal0, paramVal1 });
Pinning data into the cache instructs the cache to not expire the pinned results when cache flushing occurs. However, pinned results will be removed from the cache if an event occurs that invalidates the results.
You can disable caching on a per-EntityManager
,
per-PersistenceManager
or
per-Query
basis:
Example 10.17. Disabling and Enabling Query Caching
JPA:
import org.apache.openjpa.persistence.*; ... // temporarily disable query caching for all queries created from em OpenJPAEntityManager oem = OpenJPAPersistence.cast (em); oem.getFetchPlan ().setQueryResultCache (false); // re-enable caching for a particular query OpenJPAQuery oq = oem.createQuery (...); oq.getFetchPlan ().setQueryResultCache (true);
JDO:
import kodo.jdo.*; ... // temporarily disable query caching for all queries created from pm PersistenceManager pm = ...; KodoFetchPlan fetch = (KodoFetchPlan) pm.getFetchPlan (); fetch.setQueryResultCache (false); // re-enable caching for a particular query Query q = pm.newQuery (...); KodoFetchPlan fetch = KodoJDOHelper.cast (pm.getFetchPlan ()); fetch.setQueryResultCache (true);
Kodo includes built-in integrations with Tangosol Coherence and GemStone GemFire caching products.
The Kodo data cache can integrate with Tangosol's Coherence caching
system. To use Tangosol integration, set the
kodo.DataCache
configuration property to tangosol
, with
the appropriate plugin properties for your Tangosol setup. For
example:
Example 10.18. Tangosol Cache Configuration
JPA XML format:
<property name="kodo.DataCache" value="tangosol(TangosolCacheName=kodo)"/>
JDO properties format:
kodo.DataCache: tangosol(TangosolCacheName=kodo)
The Tangosol cache understands the following properties:
TangosolCacheName
: The name of the
Tangosol Coherence cache to use. Defaults to
kodo
.
TangosolCacheType
: The type of Tangosol
Coherence cache to use (optional). Valid values are
named
, distributed
, or
replicated
. Defaults to
named
, which means that the cache is looked
up via the
com.tangosol.net.CacheFactory.getCache(String)
method. This method looks up the cached by name as
defined in the Coherence configuration.
Note | |
---|---|
If you encounter problems using a Tangosol Coherence 1.2.2 distributed cache type with the Apple's OS X JVM, try using their replicated cache instead. |
ClearOnClose
: Whether the Tangosol
named cache should be completely cleared when the
EntityManagerFactory
or
PersistenceManagerFactory
is closed. Defaults to false
.
The Kodo query cache can also integrate with Tangosol's Coherence
caching system. To use Tangosol query cache integration, set the
kodo.QueryCache
configuration property to tangosol
,
with the appropriate plugin properties for your Tangosol setup.
For example:
Example 10.19. Tangosol Query Cache Configuration
JPA XML format:
<property name="kodo.QueryCache" value="tangosol(TangosolCacheName=kodo-query)"/>
JDO properties format:
kodo.QueryCache: tangosol(TangosolCacheName=kodo-query)
The Tangosol query cache understands the same properties as the
data cache, with a default Tangosol cache name of
kodo-query
.
The Kodo data cache can integrate with GemStone's GemFire v5.0.1
caching system
and later. To use GemFire in Kodo you will need to ensure that
the copy-on-read
attribute of the
region
element is set to
false
.
By default, the GemFire data cache will use a GemFire region of
root/kodo-data-cache
and the GemFire query cache
will use a region of root/kodo-query-cache
. This
can be changed be setting the optional property
GemFireCacheName
.
Example 10.20. GemFire gemfire.xml example
The following example is of a gemfire.xml
file that uses the default names for the query and data caches:
<?xml version="1.0" encoding="UTF-8"?> <!-- Gemfire cache configuration for Kodo testing --> <!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 5.0//EN" "http://www.gemstone.com/dtd/cache5_0.dtd"> <cache search-timeout="60" lock-lease="300" copy-on-read="false"> <region name="root"> <region-attributes scope="local"/> <region name="kodo-data-cache"> <region-attributes scope="local"/> </region> <region name="kodo-query-cache"> <region-attributes scope="local"/> </region> </region> </cache>
Example 10.21. GemFire Cache Configuration
JPA
persistence.xml
:
<property name="kodo.DataCache" value="gemfire(GemFireCacheName=/root/my-kodo-data-cache)"/> <property name="kodo.QueryCache" value="gemfire(GemFireCacheName=/root/my-kodo-query-cache)"/>
JDO properties file:
kodo.DataCache: gemfire(GemFireCacheName=/root/My-kodo-data-cache) kodo.QueryCache: gemfire(GemFireCacheName=/root/My-kodo-query-cache)
If you set GemFire for both kodo.DataCache
and
kodo.QueryCache
you aren't required to
specify a kodo.RemoteCommitProvider
unless you
are registering your own
RemoteCommitListener
s.
Some notes regarding using GemFire with Kodo:
Custom field types mapped with externalizers or custom mappings must be serializable.
The
kodo.DynamicDataStructs
option
is not supported.
The provided data cache classes can be easily extended to
add additional functionality. If you are adding new behavior,
you should extend kodo.datacache.DataCacheImpl
.
To use your own storage mechanism, extend
kodo.datacache.AbstractDataCache
, or implement
kodo.datacache.DataCache
directly.
If you want to implement a distributed cache that uses an
unsupported method for communications, create an implementation
of kodo.event.RemoteCommitProvider
. This
process is described in greater detail in
Section 11.3.2, “Customization”.
The query cache is just as easy to extend. Add functionality by
extending the default
kodo.datacache.QueryCacheImpl
. Implement your
own storage mechanism for query results by extending
kodo.datacache.AbstractQueryCache
or implementing
the kodo.datacache.QueryCache
interface directly.
The default cache implementations do not
automatically refresh objects in other
EntityManager
s and
PersistenceManager
s when the cache is updated or
invalidated. This behavior would not be compliant with the JPA
and JDO specifications.
Invoking OpenJPAEntityManager.evict
and PersistenceManager.evict
does not result in
the corresponding data being dropped from the data cache, unless
you have set the proper configuration options as explained
above (see Example 10.10, “Automatic Data Cache Eviction”). Other
methods related to the EntityManager
and
PersistenceManager
caches also do not
effect the data cache.
The data cache assumes that it is up-to-date with respect to the datastore, so it is effectively an in-memory extension of the database. To manipulate the data cache, you should generally use the data cache facades presented in this chapter.
You must specify a kodo.event.RemoteCommitProvider
(via the
kodo.RemoteCommitProvider
property) in order to use the data cache, even when using the
cache in a single-JVM mode. When using it in a single-JVM
context, set this property to sjvm
.
When using datastore (pessimistic) transactions in concert with the distributed caching implementations, it is possible to read stale data when reading data outside a transaction.
For example, if you have two JVMs (JVM A and JVM B) both communicating with each other, and JVM A obtains a data store lock on a particular object's underlying data, it is possible for JVM B to load the data from the cache without going to the datastore, and therefore load data that should be locked. This will only happen if JVM B attempts to read data that is already in its cache during the period between when JVM A locked the data and JVM B received and processed the invalidation notification.
This problem is impossible to solve without putting together a two-phase commit system for cache notifications, which would add significant overhead to the caching implementation. As a result, we recommend that people use optimistic locking when using data caching. If you do not, then understand that some of your non-transactional data may not be consistent with the datastore.
Note that when loading objects in a transaction, the appropriate datastore transactions will be obtained. So, transactional code will maintain its integrity.
Extent
s are not cached. So, if you plan
on iterating over a list of all the objects in an
Extent
on a regular basis, you will
only benefit from caching if you do so with a
Query
instead:
Example 10.22. Query Replaces Extent
JPA:
import org.apache.openjpa.persistence.*; ... OpenJPAEntityManager oem = OpenJPAPersistence.cast (em); Extent extent = oem.getExtent (Magazine.class, false); // This iterator does not benefit from caching... Iterator uncachedIterator = extent.iterator (); // ... but this one does. OpenJPAQuery extentQuery = oem.createQuery (...); extentQuery.setSubclasses (false); Iterator cachedIterator = extentQuery.getResultList ().iterator ();
JDO:
Extent extent = pm.getExtent (Magazine.class, false); // This iterator does not benefit from caching... Iterator uncachedIterator = extent.iterator (); // ... but this one does. Query extentQuery = pm.newQuery (extent); Iterator cachedIterator = ((List) extentQuery.execute ()).iterator ();