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<V> 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>