Caching is an essential mechanism in providing efficient usage of resources in many systems.
Data management using DataNucleus 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. DataNucleus provides a facility for caching that meets the JDO specification,
but in addition allows the use of third party caching tools (such as Oracle Coherence, EHCache,
OSCache, etc) for the level 2 caching (and also
allows user-defined Level2 caches).
This is supported by the datastore-cache plugin).
The 2 levels of caching available with DataNucleus are
-
Level 1
Cache - this is mandated by the JDO specification, and represents the caching
of instances within a PersistenceManager/EntityManager.
-
Level 2
Cache - this represents the caching of instances within a
PersistenceManagerFactory/EntityManagerFactory (across multiple PersistenceManager's)
You can think of a cache as a Map, with values referred to by keys. In the case of JDO, and of
DataNucleus, the value is the
PersistenceCapable
object and the key is the object identity.
Since identity is unique in JDO, the keys refer to unique objects. The same idea follows for JPA.
By default in DataNucleus,
Level 1
Cache is enabled. You cannot turn off the
Level 1
Cache. There are inbuilt
types for the Level 1 Cache available for selection.
DataNucleus supports the following types of
Level 1
Cache.
-
weak
- uses a weak reference backing map. If JVM garbage collection clears
the reference, then the object is removed from the cache.
This is the default Level 1 Cache
-
soft
- uses a soft reference backing map. If the map entry value object is
not being actively used, then garbage collection
may
garbage collect the reference,
in which case the object is removed from the cache.
-
hard
- uses a normal HashMap backing. With this option all references are
strong meaning that objects stay in the cache until they are explicitly removed by calling
remove() on the cache.
You can specify the type of
Level 1
Cache by providing the PMF property
datanucleus.cache.level1.type
.
You set this to the value of the type required. If you want to remove objects from the L1 cache programmatically
you should use
pm.evict()
or
pm.evictAll()
.
By default in DataNucleus,
Level 2
Cache is disabled. The user can configure the
Level 2
Cache if they so wish. This is controlled by use of the persistence property
datanucleus.cache.level2
which is a boolean property and turns on/off the use of the
Level 2
Cache. There are inbuilt default types of Level 1 and Level 2 caching in
DataNucleus.
With the
Level 2
Cache you currently have the following options.
-
default
- the default option. Provides support for the
JDO 2 interface of being able to pin objects into the cache, and unpin them when required.
This option does not support distributed caching, solely running within the JVM of the client
application. Weak references are held to non pinned objects.
-
soft
- Provides support for the JDO 2 interface of being
able to pin objects into the cache, and unpin them when required. This option does not support
distributed caching, solely running within the JVM of the client application. Soft references
are held to non pinned objects.
-
EHCache - a simple wrapper to EHCache's caching
product. Provides basic support for adding items to the cache and retrieval from the cache.
Doesn't support pinning and unpinning.
-
EHCacheClassBased - similar to the EHCache option
but class-based.
-
OSCache - a simple wrapper to OSCache's caching
product. Provides basic support for adding items to the cache and retrieval from the cache.
Doesn't support pinning and unpinning.
-
SwarmCache - a simple wrapper to SwarmCache's
caching product. Provides basic support for adding items to the cache and retrieval from the
cache. Doesn't support pinning and unpinning.
-
Oracle Coherence - a simple wrapper to Oracle's
Coherence caching product. Provides basic support for adding items to the cache and retrieval
from the cache. Doesn't support pinning and unpinning. Oracle's caches support distributed
caching, so you could, in principle, use DataNucleus in a distributed environment with
this option.
You can specify the type of
Level 2
Cache by providing the persistence property
datanucleus.cache.level2.type
. You set this to the value of the type required, see above.
If we do not have support for a particular third party caching product, you can easily extend
an interface
datanucleus.cache.Level2Cache
to provide support. If you do this for a product
that we don't have a plugin for, why not contribute it so we can include it in a future release ?
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 JDO2 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
-
evict
- used to remove objects from the Level 2 Cache
-
pin
- used to pin objects into the cache, meaning that they will not get removed by
garbage collection, and will remain in the Level 2 cache until removed.
-
unpin
- used to reverse the effects of pinning an object in the Level 2 cache.
This will mean that the object can thereafter be garbage collected if not being used.
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.
If an object is in the Level 2 cache and it is updated, these updates will also be stored in the
Level 2 cache. Similarly if you have an object in the Level 2 cache which has some fields that
haven't been loaded (due to not being in the FetchPlan for example), then any loading of these
fields will also result in the object in the L2 cache being updated.