JDBC Caching

Introduction

Ehcache can easily be combined with your existing JDBC code. Whether you access JDBC directly, or have a DAO/DAL layer, Ehcache can be combined with your existing data access pattern to speed up frequently accessed data to reduce page load times, improve performance, and reduce load from your database.

This page discusses how to add caching to a JDBC application using the commonly used DAO/DAL layer patterns.

Adding JDBC caching to a DAO/DAL layer

If your application already has a DAO/DAL layer, this is a natural place to add caching. To add caching, follow these steps:

  • identify methods which can be cached
  • instantiate a cache and add a member variable to your DAO to hold a reference to it
  • Put and get values from the cache

Identifying methods which can be cached

Normally, you will want to cache the following kinds of method calls:

  • Any method which retrieves entities by an Id
  • Any queries which can be tolerate some inconsistent or out of date data

Example methods that are commonly cacheable:

public V getById(final K id);
public Collection findXXX(...);

Instantiate a cache and add a member variable

Your DAO is probably already being managed by Spring or Guice, so simply add a setter method to your DAO layer such as setCache(Cache cache). Configure the cache as a bean in your Spring or Guice config, and then use the the frameworks injection methodology to inject an instance of the cache.

If you are not using a DI framework such as Spring or Guice, then you will need to instantiate the cache during the bootstrap of your application. As your DAO layer is being instantiated, pass the cache instance to it.

Put and get values from the cache

Now that your DAO layer has a cache reference, you can start to use it. You will want to consider using the cache using one of two standard cache access patterns:

  • cache-aside
  • cache-as-sor

You can read more about these in these cache-as-sor and Concepts cache-aside sections.

Putting it all together - an example

Here is some example code that demonstrates a DAO based cache using a cache aside methodology wiring it together with Spring.

This code implements a PetDao modeled after the Spring Framework PetClinic sample application.

It implements a standard pattern of creating an abstract GenericDao implementation which all Dao implementations will extend.

It also uses Spring's SimpleJdbcTemplate to make the job of accessing the database easier.

Finally, to make Ehcache easier to work with in Spring, it implements a wrapper that holds the cache name.

The example files

The following are relevant snippets from the example files. A full project will be available shortly.

CacheWrapper.java

Simple get/put wrapper interface.

public interface CacheWrapper<K, V> 
{
 void put(K key, V value);
 V get(K key);
}

EhcacheWrapper.java

The wrapper implementation. Holds the name so caches can be named.

public class EhCacheWrapper<K, V> implements CacheWrapper<K, V> 
{
    private final String cacheName;
    private final CacheManager cacheManager;
    public EhCacheWrapper(final String cacheName, final CacheManager cacheManager)
    {
    this.cacheName = cacheName;
    this.cacheManager = cacheManager;
    }
    public void put(final K key, final V value)
    {
    getCache().put(new Element(key, value));
    }
    public V get(final K key, CacheEntryAdapter<V> adapter) 
    {
    Element element = getCache().get(key);
    if (element != null) {
        return (V) element.getValue();
    }
    return null;
    }
    public Ehcache getCache() 
    {
    return cacheManager.getEhcache(cacheName);
    }
}

GenericDao.java

The Generic Dao. It implements most of the work.

public abstract class GenericDao<K, V extends BaseEntity> implements Dao<K, V>
{
    protected DataSource datasource;
    protected SimpleJdbcTemplate jdbcTemplate;
    /* Here is the cache reference */
    protected CacheWrapper<K, V> cache;
    public void setJdbcTemplate(final SimpleJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    public void setDatasource(final DataSource datasource) {
        this.datasource = datasource;
    }
    public void setCache(final CacheWrapper<K, V> cache) {
        this.cache = cache;
    }
    /* the cacheable method */
    public V getById(final K id) {
    V value;
    if ((value = cache.get(id)) == null) {
        value = this.jdbcTemplate.queryForObject(findById, mapper, id);
        if (value != null) {
        cache.put(id, value);
        }
    }
    return value;
    }
    /** rest of GenericDao implementation here **/
    /** ... **/
    /** ... **/
    /** ... **/
}

PetDaoImpl.java

The Pet Dao implementation, really it doesn't need to do anything unless specific methods not available via GenericDao are cacheable.

public class PetDaoImpl extends GenericDao<Integer, Pet> implements PetDao 
{
/** ... **/
}

We need to configure the JdbcTemplate, Datasource, CacheManager, PetDao, and the Pet cache using the spring configuration file.

application.xml

The Spring configuration file.

<!-- datasource and friends -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.FasterLazyConnectionDataSourceProxy">
  <property name="targetDataSource" ref="dataSourceTarget"/>
</bean>
<bean id="dataSourceTarget" class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close">
  <property name="user" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
  <property name="driverClass" value="${jdbc.driverClassName}"/>
  <property name="jdbcUrl" value="${jdbc.url}"/>
  <property name="initialPoolSize" value="50"/>
  <property name="maxPoolSize" value="300"/>
  <property name="minPoolSize" value="30"/>
  <property name="acquireIncrement" value="2"/>
  <property name="acquireRetryAttempts" value="0"/>
</bean>
<!-- jdbctemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
  <constructor-arg ref="dataSource"/>
</bean>
<!-- the cache manager -->
<bean id="cacheManager" class="EhCacheManagerFactoryBean">
  <property name="configLocation" value="classpath:${ehcache.config}"/>
</bean>
<!-- the pet cache to be injected into the pet dao -->
<bean name="petCache" class="EhCacheWrapper">
  <constructor-arg value="pets"/>
  <constructor-arg ref="cacheManager"/>
</bean>
<!-- the pet dao -->
<bean id="petDao" class="PetDaoImpl">
  <property name="cache" ref="petCache"/>
  <property name="jdbcTemplate" ref="jdbcTemplate"/>
  <property name="datasource" ref="dataSource"/>
</bean>