JPA Caching

Caching is an important performance improvement technique. JPA supports caching of  object data. In this chapter we are looking into the caching techniques available with JPA for caching object data.

JPA Caching

JPA supports two levels of caching. They are 1)Level 1 Cache 2)Level 2 Cache. We will be looking these caching techniques in detail.

1)JPA Level 1 Cache

This is also known as EntityManager cache. As the name indicates , this cache is in the EntityManager level..The persistence context associated with each entity manager acts as the level 1 cache.If the Entity Manager is a JTA managed one, then the cache spans till the end of the transaction. After each transaction new Entity Manager  will be created. So cache will be cleared.In case of application managed Entity Manager, the Entity Manager will be hold for a long time. So the cache also will be hold for long time. This may lead to memory leaks  and stale data .So it is a good practice to have separate Entity managers for each transaction.

Suppose we are querying the database for an object. The object is not inserted to the database by the EntityManager  , because the transaction is not committed yet. In this case the query will return  null because the query is inspecting the database , not the cache. This problem can be avoided either by calling the flush() method before querying the database or by using the automatic flush triggering by the flushMode.The default flushMode is to trigger the flush.  Disabling the flushing is not a good practice.

2)JPA Level 2 Cache

JPA Level 2 cache is the shared cache.This cache was included in JPA 2.0 specification. The implementation of this cache is provider specific.If we don’t want the features of level 2 cache , then we can turn off this cache.The shared cache can be enabled / disabled for an entity using the @Cacheable annotation or by using cacheable attribute in the xml.

The Student.java given here is having the second level cache enabled.

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity(name = "Student")
@Table(name = "student")
@Cacheable(true)
public class Student implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String level;

public Student() {

}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

@Column(name = "name")
public void setName(String name) {
this.name = name;
}

public String getLevel() {
return level;
}

@Column(name = "level")
public void setLevel(String level) {
this.level = level;
}

public String toString() {
return "ID = " + getId() + " ; Name : = " + getName() + " ; Level : = "
+ getLevel();
}

}

Also the second level cache can be enabled\disabled in the persistence unit level also.The element in persistence.xml  can be used enable/disable the caching of entities  in a persistence unit.The SharedCacheMode enum provides the various  enum constants to enable/disable caching for a persistence unit.

The enum constants in SharedCacheMode enum are:

    • NONE
    • ALL
    • DISABLE_SELECTIVE
    • ENABLE_SELECTIVE
    • UNSPECIFIED

If we use NONE for a persistence unit, then the second level cache will be turned off fully.If we specify the shared cache mode as ALL , then caching will be enabled for all the entities in that persistence unit.In case of UNSPECIFIED ,caching behavior is undefined and provider specific defaults may be applicable.In case of ENABLE_SELECTIVE , caching will be enabled for all entities with @Cacheable(true).If we specify DISABLE_SELECTIVE as shared cache mode , then caching will be enabled for all entities except the entities with @Cacheable(false).

Example: Consider the given persistence.xml file. It has one persistence unit.

org.apache.openjpa.persistence.PersistenceProviderImpl ENABLE_SELECTIVE
----------------------------------------------------------

-------------------------------------------------------------

The     ENABLE_SELECTIVE element enables caching for all entities with @Cacheable(true) in  persistence unit OpenJPASample .

If we use second level cache , then we should have some level of tolerance to stale data.

Bypassing/Refreshing Cache

From JPA 2.0 onwards JPA Query hints provides options to bypass or refresh the cache.The query hints are javax.persistence.cache.retrieveMode and javax.persistence.cache.storeMode. The CacheRetrieveMode enum class defines the values for javax.persistence.cache.retrieveMode and CacheStoreMode enum defines the values for  the query hint javax.persistence.cache.retrieveMode.

Enum constants in  CacheRetrieveMode

There are two enum constants. BYPASS and USE.If  we specify the javax.persistence.cache.retrieveMode query hint value as BYPASS , then the cache will be bypassed and the data will be fetched from the database. If we specify the value as USE , then the entity data will be read from the cache.

Example:

Query query = entitymanager.createQuery("select student FROM Student student");
query.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);

Enum Constants in CacheStoreMode

There are three enum constants in CacheStoreMode.java. They are BYPASS,REFRESH and USE.

BYPASS: Entity data is not inserted into cache when read data from database or when committed data to database.

USE: Entity data is inserted or updated  into cache when read/ committed.This is the default behavior.

REFRESH : Entity data is inserted or updated  into cache when read / committed .

Cache Invalidation in JPA

JPA 2.0 has a Cache interface. The reference can be obtained by using getCache() method EntityManagerFactory  . The cache has an evict() method , which is used to invalidate the cache manually.

Example:

Cache cache = entityManagerFactory.getCache();
cache.evict(Student.class, 1);

The above code snippet invalidates the Student object with id value 1.

It is better if we have a cache invalidation mechanism which does invalidation in a specified time interval rather than doing the invalidation from the code .JPA 2.0 specification does not define any  such configurable mechanism. Some providers like EclipseLink provides a configurable invalidation mechanism. In case of  EclipseLink , the @Cache annotation or the tag in the orm file can be used to configure the cache invalidation.

JPA Query cache

A query cache caches query results instead of objects.In case of query cache , the cache key is not the object id. Instead  the  cache key will be based on the query and the parameters. So if we have a named query that is frequently executing , after the  first execution the query result will be cached. And subsequent queries not needed to hit the database. Instead , the result will be fetching from the query cache.Usually query cache is having invalidation options too. Enabling/Disabling options vary for each provider.

See Related Discussions

JPA Overview

JPA Example – Insert/Read

find method in JPA example

JPA Update Example

JPA Delete EXample

JPQL

JPQL Update Query Example

JPQL Delete Query

Locking in JPA

Locking in JPA

Relational Mappings in JPA

One To One Mapping in JPA

One To Many mapping in JPA

Many To Many Mapping in JPA

Many To One Mapping in JPA