JPA Caching

Caching is an essential mechanism in providing efficient usage of resources in many systems. Caching allows objects to be retained and returned rapidly without having to make an extra call to the datastore. JPA1 defines very little about caching, but the JPA2 specification improves on this. DataNucleus provides a definition of caching at 2 levels. The 2 levels of caching available are

  • Level 1 Cache - represents the caching of instances within an EntityManager
  • Level 2 Cache - represents the caching of instances within an EntityManagerFactory (across multiple EntityManager's)

You can think of a cache as a Map, with values referred to by keys. In the case of JPA, the key is the object identity (identity is unique in JPA).



Level 1 Cache

The Level 1 Cache is always enabled. 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.
  • 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.
  • strong - 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 all objects from the L1 cache programmatically you should use em.clear() but bear in mind the other things that this does.

Objects are placed in the L1 cache (and updated there) during the course of the transaction. This provides rapid access to the objects in use in the users application and is used to guarantee that there is only one object with a particular identity at any one time for that EntityManager.

The Level 1 cache is a DataNucleus plugin point allowing you to provide your own cache where you require it.



Level 2 Cache

By default 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.

With the Level 2 Cache you currently have the following options.

  • none - turn OFF Level 2 caching.
  • weak - use the internal (weak reference based) L2 cache. Provides support for the JPA2 interface of being able to put objects into the cache, and evict 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 - use the internal (soft reference based) L2 cache. Provides support for the JPA2 interface of being able to put objects into the cache, and evict 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.
  • javax.cache - a simple wrapper to javax.cache's caching product. Provides basic support for adding items to the cache and retrieval from the cache. Doesn't support pinning and unpinning.
  • memcache - a simple wrapper to Memcache's caching product. Provides basic support for adding items to the cache and retrieval from the cache. Doesn't support pinning and unpinning.

The EHCache, OSCache, SwarmCache, Coherence, javax.cache, and Memcache caches are available in the datanucleus-cache plugin.

Objects are placed in the L2 cache when you commit() the transaction of a EntityManager. 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.



Controlling the Level 2 Cache

The majority of times when using a JPA-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 JPA2 and DataNucleus you have the ability to control which objects remain in the cache. This is available via a method on the EntityManagerFactory .

EntityManagerFactory emf = Persistence.createEntityManagerFactory(persUnitName, props);
Cache cache = emf.getCache();

The Cache interface provides methods to control the retention of objects in the cache. You have 2 types of methods

  • contains - check if an object of a type with a particular identity is in the cache
  • evict - used to remove objects from the Level 2 Cache

You can also control which classes are put into a Level 2 cache. So with the following JPA2 annotation @Cacheable, no objects of type MyClass will be put in the L2 cache.

@Cacheable(false)
@Entity
public class MyClass
{
    ...
}

If you want to control which fields of an object are put in the Level 2 cache you can do this using an extension annotation on the field. This setting is only required for fields that are relationships to other persistable objects. Like this

public class MyClass
{
    ...

    Collection values;

    @Extension(vendorName="datanucleus", key="cacheable", value="false")
    Collection elements;
}

So in this example we will cache "values" but not "elements". If a field is cacheable then

  • If it is a persistable object, the "identity" of the related object will be stored in the Level 2 cache for this field of this object
  • If it is a Collection of persistable elements, the "identity" of the elements will be stored in the Level 2 cache for this field of this object
  • If it is a Map of persistable keys/values, the "identity" of the keys/values will be stored in the Level 2 cache for this field of this object
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.





L2 Cache using javax.cache

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}


L2 Cache using Oracle Coherence

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). You have the benefits of Coherence'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 tangosolCache = ((TangosolLevel2Cache)cache).getTangosolCache();


L2 Cache using EHCache

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".



L2 Cache using OSCache

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}


L2 Cache using SwarmCache

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}


L2 Cache using Memcache

DataNucleus provides a simple wrapper to Memcache caches. To enable this you should set the persistence properties

    datanucleus.cache.level2.type=memcache
    datanucleus.cache.level2.memcache.servers=...
    datanucleus.cache.level2.memcache.keyprefix=...
    datanucleus.cache.level2.memcache.expireSeconds=...

datanucleus.cache.level2.memcache.servers is a space separated list of memcache hosts/ports, e.g. host:port host2:port. datanucleus.cache.level2.memcache.keyprefix is a string to prefix all keys with to ensure there are no clashes with other application uses of memcache. datanucleus.cache.level2.memcache.expireSeconds if not set or set to 0 then no expire