Caching is an essential mechanism in providing efficient usage of resources in many systems. Data management using JDO is no different and provides a definition of caching at 2 levels. Caching allows objects to be retained and returned rapidly without having to make an extra call to the datastore. The 2 levels of caching available with DataNucleus are
You can think of a cache as a Map, with values referred to by keys. In the case of JDO, the key is the object identity (identity is unique in JDO).
By default the Level 2 Cache is enabled. The user can configure the Level 2 Cache if they so wish. This is controlled by use of the persistence property datanucleus.cache.level2.type. You set this to "type" of cache required. With the Level 2 Cache you currently have the following options.
The javax.cache cache is available in the datanucleus-core plugin. The EHCache, OSCache, SwarmCache, Coherence, JCache, Cacheonix, and Memcache caches are available in the datanucleus-cache plugin.
In addition you can control the mode of operation of the L2 cache. You do this using the persistence property datanucleus.cache.level2.mode. The default is UNSPECIFIED which means that DataNucleus will cache all objects of entities unless the entity is explicitly marked as not cacheable. The other options are NONE (don't cache ever), ALL (cache all entities regardless of annotations), ENABLE_SELECTIVE (cache entities explicitly marked as cacheable), or DISABLE_SELECTIVE (cache entities unless explicitly marked as not cacheable - i.e same as our default).
Objects are placed in the L2 cache when you commit() the transaction of a PersistenceManager. This means that you only have datastore-persisted objects in that cache. Also, if an object is deleted during a transaction then at commit it will be removed from the L2 cache if it is present.
The Level 2 cache is a DataNucleus plugin point allowing you to provide your own cache
where you require it. Use the examples of the EHCache, Coherence caches etc as reference.
Note that you can have a PMF with L2 caching enabled yet have a PM with it disabled. This is achieved by creating the PM as you would normally, and then call
pm.setProperty("datanucleus.cache.level2.type", "none");
The majority of times when using a JDO-enabled system you will not have to take control over any aspect of the caching other than specification of whether to use a Level 2 Cache or not. With JDO and DataNucleus you have the ability to control which objects remain in the cache. This is available via a method on the PersistenceManagerFactory.
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props); DataStoreCache cache = pmf.getDataStoreCache();
The DataStoreCache interface
provides methods to control the retention of objects in the cache. You have 3 groups of methods
These methods can be called to pin objects into the cache that will be much used. Clearly this will be very much application dependent, but it provides a mechanism for users to exploit the caching features of JDO. If an object is not "pinned" into the L2 cache then it can typically be garbage collected at any time, so you should utilise the pinning capability for objects that you wish to retain access to during your application lifetime. For example, if you have an object that you want to be found from the cache you can do
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props); DataStoreCache cache = pmf.getDataStoreCache(); cache.pinAll(MyClass.class, false); // Pin all objects of type MyClass from now on PersistenceManager pm = pmf.getPersistenceManager(); Transaction tx = pm.currentTransaction(); try { tx.begin(); pm.makePersistent(myObject); // "myObject" will now be pinned since we are pinning all objects of type MyClass. tx.commit(); } finally { if (tx.isActive()) { tx.close(); } }
Thereafter, whenever something refers to myObject, it will find it in the Level 2 cache. To turn this behaviour off, the user can either unpin it or evict it.
JDO allows control over which classes are put into a Level 2 cache. You do this by specifying the cacheable attribute to false (defaults to true). So with the following specification, no objects of type MyClass will be put in the L2 cache.
Using XML: <class name="MyClass" cacheable="false"> ... </class> Using Annotations: @Cacheable("false") public class MyClass { ... }
JDO allows you control over which fields of an object are put in the Level 2 cache. You do this by specifying the cacheable attribute to false (defaults to true). This setting is only required for fields that are relationships to other persistable objects. Like this
Using XML: <class name="MyClass"> <field name="values"/> <field name="elements" cacheable="false"/> ... </class> Using Annotations: public class MyClass { ... Collection values; @Cacheable("false") Collection elements; }
So in this example we will cache "values" but not "elements". If a field is cacheable then
When pulling an object in from the Level 2 cache and it has a reference to another object Access Platform uses the "identity" to find that object in the Level 1 or Level 2 caches to re-relate the objects.
DataNucleus has an extension in metadata allowing the user to define that all instances of a class are automatically pinned in the Level 2 cache.
@PersistenceCapable @Extension(vendorName="datanucleus", key="cache-pin", value="true") public class MyClass { ... }
DataNucleus provides a simple wrapper to javax.cache's caches. To enable this you should set the persistence properties
datanucleus.cache.level2.type=javax.cache datanucleus.cache.level2.cacheName={cache name} datanucleus.cache.level2.timeout={expiration time in millis - optional}
DataNucleus provides a simple wrapper to JCache's caches. This is an old version of what will become javax.cache (separate option). To enable this you should set the persistence properties
datanucleus.cache.level2.type=jcache datanucleus.cache.level2.cacheName={cache name} datanucleus.cache.level2.timeout={expiration time in millis - optional}
DataNucleus provides a simple wrapper to Oracle's Coherence caches. This currently takes the NamedCache interface in Coherence and instantiates a cache of a user provided name. To enabled this you should set the following persistence properties
datanucleus.cache.level2.type=coherence datanucleus.cache.level2.cacheName={coherence cache name}
The Coherence cache name is the name that you would normally put into a call to CacheFactory.getCache(name). As mentioned earlier, this cache does not support the pin/unpin operations found in the standard JDO interface. However you do have the benefits of Oracle's distributed/serialized caching. If you require more control over the Coherence cache whilst using it with DataNucleus, you can just access the cache directly via
DataStoreCache cache = pmf.getDataStoreCache(); NamedCache coherenceCache = ((CoherenceLevel2Cache)cache).getCoherenceCache();
DataNucleus provides a simple wrapper to EHCache's caches. To enable this you should set the persistence properties
datanucleus.cache.level2.type=ehcache datanucleus.cache.level2.cacheName={cache name} datanucleus.cache.level2.configurationFile={EHCache configuration file (in classpath)}
The EHCache plugin also provides an alternative L2 Cache that is class-based. To use this you would need to replace "ehcache" above with "ehcacheclassbased".
DataNucleus provides a simple wrapper to OSCache's caches. To enable this you should set the persistence properties
datanucleus.cache.level2.type=oscache datanucleus.cache.level2.cacheName={cache name}
DataNucleus provides a simple wrapper to SwarmCache's caches. To enable this you should set the persistence properties
datanucleus.cache.level2.type=swarmcache datanucleus.cache.level2.cacheName={cache name}
DataNucleus provides a simple wrapper to Spymemcached caches and Xmemcached caches. To enable this you should set the persistence properties
datanucleus.cache.level2.type=spymemcached [or "xmemcached"] datanucleus.cache.level2.cacheName={prefix for keys, to avoid clashes with other memcached objects} datanucleus.cache.level2.memcached.servers=... datanucleus.cache.level2.memcached.expireSeconds=...
datanucleus.cache.level2.memcached.servers is a space separated list of memcached hosts/ports, e.g. host:port host2:port. datanucleus.cache.level2.memcached.expireSeconds if not set or set to 0 then no expire
DataNucleus provides a simple wrapper to Cacheonix. To enable this you should set the persistence properties
datanucleus.cache.level2.type=cacheonix datanucleus.cache.level2.cacheName={cache name}
Note that you can optionally also specify
datanucleus.cache.level2.timeout={timeout-in-millis (default=60)} datanucleus.cache.level2.configurationFile={Cacheonix configuration file (in classpath)}
and define a cacheonix-config.xml like
<?xml version="1.0"?> <cacheonix> <local> <!-- One cache per class being stored. --> <localCache name="mydomain.MyClass"> <store> <lru maxElements="1000" maxBytes="1mb"/> <expiration timeToLive="60s"/> </store> </localCache> <!-- Fallback cache for classes indeterminable from their id. --> <localCache name="datanucleus"> <store> <lru maxElements="1000" maxBytes="10mb"/> <expiration timeToLive="60s"/> </store> </localCache> <localCache name="default" template="true"> <store> <lru maxElements="10" maxBytes="10mb"/> <overflowToDisk maxOverflowBytes="1mb"/> <expiration timeToLive="1s"/> </store> </localCache> </local> </cacheonix>