JPA Shared (L2) Entity Cache

Every EntityManagerjakarta.persistence.EntityManagerInterface used to interact with the persistence context. has a persistence context, which is a collection of all the entities that it manages. The persistence context serves as a first-level cache. Retrieving an entity that the EntityManager already manages returns the existing instance from the persistence context, not a newly instantiated one.

The persistence context is scoped to a single EntityManagerjakarta.persistence.EntityManagerInterface used to interact with the persistence context.. This section describes a second-level (L2) cache of entities, which is managed by the EntityManagerFactoryjakarta.persistence.EntityManagerFactoryInterface used to interact with the persistence unit, and to create new instances of EntityManager . and shared by all its EntityManager instances. The broader scope of this cache makes it useful in applications that use many short-term EntityManager instances.

In addition to the EntityManager's L1 cache and the EntityManagerFactory's L2 cache, which are managed on the client side, ObjectDB also manages several server-side caches:

The scope of these server-side caches is wider because they exist per database and are shared by all EntityManagerFactory and EntityManager instances for that database, even across different client computers.

Setting the shared cache

You can configure the shared (L2) cache at three levels:

  • Globally in the ObjectDB configuration.
  • Per persistence unit in the persistence.xml file.
  • Per entity class, using annotations.

ObjectDB Configuration

The shared cache size is specified in the ObjectDB configuration:

    <cache ... level2="0mb" />

The level2 attribute determines the size of the EntityManagerFactory's shared cache. The default size, 0, disables the cache. To enable the cache, you must specify a positive value.

Persistence Unit Settings

You can also enable or disable the shared cache by using a persistence unit property:

   <persistence-unit name="my-pu">
     ...
     <properties>
       <property name="jakarta.persistence.sharedCache.mode" value="ALL"/>
     </properties>
     ...
   </persistence-unit>

You can set the jakarta.persistence.sharedCache.mode property to one of the following values:

  • NONE: The cache is disabled.
  • ENABLE_SELECTIVE: The cache is disabled except for selected entity classes (see below).
  • DISABLE_SELECTIVE: The cache is enabled except for selected entity classes (see below).
  • ALL (the default): The cache is enabled for all entity classes.
  • UNSPECIFIED: Handled differently by JPA providers. In ObjectDB, this value is equivalent to ALL, which is the default.

If the cache size is 0, the shared cache is disabled regardless of the mode.

Entity class cache settings

The ENABLE_SELECTIVE mode disables the cache for all entity classes except those explicitly marked as @Cacheablejakarta.persistence.CacheableSpecifies whether an entity should be cached, if caching is enabled, and when the value of the persistence.xml caching element is SharedCacheMode.ENABLE_SELECTIVE or SharedCacheMode.DISABLE_SELECTIVE .. For example:

@Cacheablejakarta.persistence.CacheableSpecifies whether an entity should be cached, if caching is enabled, and when the value of the  persistence.xml  caching element is   SharedCacheMode.ENABLE_SELECTIVE   or   SharedCacheMode.DISABLE_SELECTIVE  . // or @Cacheable(true)
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity.
public class MyCacheableEntityClass {
    ...
}

Similarly, the DISABLE_SELECTIVE mode enables the cache for all entity classes except those explicitly marked as not cacheablejakarta.persistence.CacheableSpecifies whether an entity should be cached, if caching is enabled, and when the value of the persistence.xml caching element is SharedCacheMode.ENABLE_SELECTIVE or SharedCacheMode.DISABLE_SELECTIVE .. For example:

@Cacheablejakarta.persistence.CacheableSpecifies whether an entity should be cached, if caching is enabled, and when the value of the  persistence.xml  caching element is   SharedCacheMode.ENABLE_SELECTIVE   or   SharedCacheMode.DISABLE_SELECTIVE  .(false)
@Entityjakarta.persistence.EntityDeclares that the annotated class is an entity.
public class MyNonCacheableEntityClass extends MyCacheableEntityClass {
    ...
}

@Cacheable is an inherited annotation. An entity class that is not marked with @Cacheable inherits the cacheability setting from its superclass.

Using the shared cache

When enabled, the shared cache automatically provides the following functionality:

  • On retrieval: The shared cache is used for entities that are not in the persistence context. If an entity is not in the shared cache, it is retrieved from the database and then added to the shared cache.
  • On commit: New and modified entities are added to the shared cache.

JPA provides two properties that you can use to change the default behavior.

jakarta.persistence.cache.retrieveMode

The jakarta.persistence.cache.retrieveMode property specifies whether the shared cache is used on retrieval. This property accepts two values from the CacheRetrieveModejakarta.persistence.CacheRetrieveModeSpecifies how the EntityManager interacts with the second-level cache when data is read from the database via the EntityManager.find methods and execution of queries. enum:

The default setting is USEjakarta.persistence.CacheRetrieveMode.USERead entity data from the cache: this is the default behavior.. You can change it for a specific EntityManager:

  em.setPropertyjakarta.persistence.EntityManager.setProperty(String,Object)Set an entity manager property or hint.(
      "jakarta.persistence.cache.retrieveMode", CacheRetrieveModejakarta.persistence.CacheRetrieveModeSpecifies how the   EntityManager   interacts with the second-level cache when data is read from the database via the   EntityManager.find   methods and execution of queries..BYPASSjakarta.persistence.CacheRetrieveMode.BYPASSBypass the cache: get data directly from the database.);

You can also override the setting for a specific retrieval operation:

  // Before executing a query:
  query.setHintjakarta.persistence.TypedQuery.setHint(String,Object)Set a query property or hint.("jakarta.persistence.cache.retrieveMode",
      CacheRetrieveModejakarta.persistence.CacheRetrieveModeSpecifies how the   EntityManager   interacts with the second-level cache when data is read from the database via the   EntityManager.find   methods and execution of queries..BYPASSjakarta.persistence.CacheRetrieveMode.BYPASSBypass the cache: get data directly from the database.);
  // For retrieval by type and primary key:
  em.findjakarta.persistence.EntityManager.find(Class,Object,Map)Find by primary key, using the specified properties.(MyEntity2.class, Long.valueOf(1),
      Collections.<String,Object>singletonMap(
        "jakarta.persistence.cache.retrieveMode", CacheRetrieveModejakarta.persistence.CacheRetrieveModeSpecifies how the   EntityManager   interacts with the second-level cache when data is read from the database via the   EntityManager.find   methods and execution of queries..BYPASSjakarta.persistence.CacheRetrieveMode.BYPASSBypass the cache: get data directly from the database.));

jakarta.persistence.cache.storeMode

The jakarta.persistence.cache.storeMode property specifies whether to add new data to the cache on commit and retrieval. This property accepts three values from the CacheStoreModejakarta.persistence.CacheStoreModeSpecifies how the EntityManager interacts with the second-level cache when data is read from the database and when data is written to the database. enum:

The default setting is USEjakarta.persistence.CacheStoreMode.USEInsert entity data into cache when read from database and insert/update entity data when written to the database: this is the default behavior.. You can change it for a specific EntityManager:

  em.setPropertyjakarta.persistence.EntityManager.setProperty(String,Object)Set an entity manager property or hint.("jakarta.persistence.cache.storeMode", CacheStoreModejakarta.persistence.CacheStoreModeSpecifies how the   EntityManager   interacts with the second-level cache when data is read from the database and when data is written to the database..BYPASSjakarta.persistence.CacheStoreMode.BYPASSDon't insert into cache.);

You can also override the setting for a specific retrieval operation. For example:

  em.findjakarta.persistence.EntityManager.find(Class,Object,Map)Find by primary key, using the specified properties.(MyEntity2.class, Long.valueOf(1),
      Collections.<String,Object>singletonMap(
          "jakarta.persistence.cache.storeMode", CacheRetrieveModejakarta.persistence.CacheRetrieveModeSpecifies how the   EntityManager   interacts with the second-level cache when data is read from the database via the   EntityManager.find   methods and execution of queries..BYPASSjakarta.persistence.CacheStoreMode.BYPASSDon't insert into cache.));

The difference between CacheStoreMode.USE and CacheStoreMode.REFRESH is apparent when you bypass the cache during retrieval operations. In this case, an entity that is already cached is updated with the freshly retrieved data only when you use CacheStoreMode.REFRESH. This can be useful when other applications or other EntityManagerFactory instances might update the database.

Using the cache interface

The shared cache is represented by the Cachejakarta.persistence.CacheInterface used to interact with the second-level cache. interface. You can get a Cache instance by using the EntityManagerFactoryjakarta.persistence.EntityManagerFactoryInterface used to interact with the persistence unit, and to create new instances of EntityManager .'s getCache()jakarta.persistence.EntityManagerFactory.getCache()Access the cache that is associated with the entity manager factory (the "second level cache"). method:

  Cachejakarta.persistence.CacheInterface used to interact with the second-level cache. cache = emf.getCachejakarta.persistence.EntityManagerFactory.getCache()Access the cache that is associated with the entity manager factory (the "second level cache").();

The Cache object lets you check if a specified entity is cached:

  boolean isCached = cache.containsjakarta.persistence.Cache.contains(Class,Object)Whether the cache contains data for the given entity.(MyEntity.class, Long.valueOf(id));

You can remove cached entities from the cache by using one of the evict methods:

  // Remove a specific entity from the shared cache:
  cache.evictjakarta.persistence.Cache.evict(Class,Object)Remove the data for the given entity from the cache.(MyEntity.class, Long.valueOf(id));
  // Remove all the instances of a specific class from the cache:
  cache.evictjakarta.persistence.Cache.evict(Class)Remove the data for entities of the specified class (and its subclasses) from the cache.(MyEntity.class);
  // Clear the shared cache by removing all the cached entities:
  cache.evictAlljakarta.persistence.Cache.evictAll()Clear the cache.();

In most applications, you do not need to use the Cachejakarta.persistence.CacheInterface used to interact with the second-level cache. interface and its methods directly.