Integrating Spring and EHCache

Using Spring Modules and EHCache, one can transparently cache method results. Spring Modules uses a proxy which intercepts call to the method of bean; consults the cache to check if method was called with same parameters before, if so will return cached result.

EHCache is the actual provider of caching solution and Spring Module handles method interception and result storing in cache.

Since I am using Maven2, so added following dependancies –

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.springmodules</groupId>
            <artifactId>spring-modules-cache</artifactId>
            <version>0.9</version>
        </dependency>

My complete pom.xml –

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>Spring</groupId>
    <artifactId>Spring</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.10</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.springmodules</groupId>
            <artifactId>spring-modules-cache</artifactId>
            <version>0.9</version>
        </dependency>
    </dependencies>
</project>

Contact Table sql –

 CREATE TABLE `contact` (
  `ID` int(15) NOT NULL AUTO_INCREMENT,
  `FIRSTNAME` varchar(50) DEFAULT NULL,
  `LASTNAME` varchar(50) DEFAULT NULL,
  `EMAIL` varchar(150) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `EMAIL` (`EMAIL`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

Adding ehcache namespace to Spring config file –

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
...
</beans>

Created ehcache.xml file in classpath.

<ehcache>
    <defaultCache
            maxElementsInMemory="500" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="LFU"/>
    <cache name="contactCache" maxElementsInMemory="500" eternal="true" overflowToDisk="false"
           memoryStoreEvictionPolicy="LFU"/>
</ehcache>

Adding caching to application-context.xml –

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <ehcache:config configLocation="classpath:ehcache.xml" />

    <ehcache:proxy id="contactDAO" refId="contactDAOTarget">
        <ehcache:caching cacheName="contactCache" methodName="findAll"/>
        <ehcache:flushing cacheNames="contactCache" methodName="createContact" when="after"/>
    </ehcache:proxy>

    <bean id="contactDAOTarget" class="byteco.de.spring.ContactDAOImpl">
        <property name="namedParameterJdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
        <property name="initialSize" value="5"/>
        <property name="url" value="jdbc:mysql://localhost:3306/springjpa"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg type="javax.sql.DataSource" ref="datasource"/>
    </bean>
</beans>

Here is my test class –

package byteco.de.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.test.annotation.ExpectedException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

import static org.junit.Assert.assertTrue;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "/application-context.xml")
public class ContactDAOTest {

    @Autowired
    ContactDAO contactDAO;

    @Test
    public void shouldCreateNewContact(){
         assertTrue(contactDAO.createContact("gabbar","singh","gabbar@singh.com"));
         assertTrue(contactDAO.createContact("kalia","singh","kalia@singh.com"));
    }

    @Test
    public void shouldReturnListOfContact(){
        assertTrue(contactDAO.findAll().size()>0);
        assertTrue(contactDAO.findAll().size()>0);
        assertTrue(contactDAO.findAll().size()>0);
        // This will cause the cache to be cleared. refer to logs.
        assertTrue(contactDAO.createContact("samba","singh","samba@singh.com"));
        assertTrue(contactDAO.findAll().size()>0);
        assertTrue(contactDAO.findAll().size()>0);
        assertTrue(contactDAO.findAll().size()>0);
    }
}

Executed test shouldCreateNewContact first then shouldReturnListOfContact. Below is the console output –

DEBUG [org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor] - Autowiring by type from bean name 'byteco.de.spring.ContactDAOTest' to bean named 'contactDAO'
DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0'
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <null>
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL query
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [select * from contact]
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to store the object <[0. 1 gabbar singh , 1. 2 kalia singh ]> in the cache using key <2056181503|2056171012> and model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Object was successfully stored in the cache
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <[0. 1 gabbar singh , 1. 2 kalia singh ]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <[0. 1 gabbar singh , 1. 2 kalia singh ]>
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL update
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [insert into contact (firstname,lastname,email) values (?,?,?)]
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to flush the cache using model <org.springmodules.cache.provider.ehcache.EhCacheFlushingModel@2dd59d3c[cacheNames={'contactCache'}, flushBeforeMethodExecution=false]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Cache has been flushed.
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <null>
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL query
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [select * from contact]
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to store the object <[0. 1 gabbar singh , 1. 2 kalia singh , 2. 3 samba singh ]> in the cache using key <2056181503|2056171012> and model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Object was successfully stored in the cache
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <[0. 1 gabbar singh , 1. 2 kalia singh , 2. 3 samba singh ]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Attempt to retrieve a cache entry using key <2056181503|2056171012> and cache model <org.springmodules.cache.provider.ehcache.EhCacheCachingModel@1531164[cacheName='contactCache', blocking=false, cacheEntryFactory=null]>
DEBUG [org.springmodules.cache.provider.ehcache.EhCacheFacade] - Retrieved cache element <[0. 1 gabbar singh , 1. 2 kalia singh , 2. 3 samba singh ]>

Here are my DAO classes –

package byteco.de.spring;
import java.util.List;
public interface ContactDAO {
    boolean createContact(String firstName, String lastName, String email);
    List findAll();
}
package byteco.de.spring;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContactDAOImpl implements ContactDAO {

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    public boolean createContact(String firstName, String lastName, String email) {
        Map<String,String> map = new HashMap<String, String>();
        map.put("firstname",firstName);
        map.put("lastname",lastName);
        map.put("email",email);
        int i = namedParameterJdbcTemplate.update("insert into contact (firstname,lastname,email) values (:firstname,:lastname,:email)", map);
        return i>0;
    }

    public List findAll() {
        return namedParameterJdbcTemplate.query("select * from contact", (Map<String, ?>) null, new RowMapper() {
            public Object mapRow(ResultSet resultSet, int i) throws SQLException {
                return i + ". " + resultSet.getString(1) + " " + resultSet.getString(2) + " " + resultSet.getString(3) + " ";
            }
        });
    }
}

We are caching the result of findAll and clearing cache when createContact is executed.




Comments

  1. Christian HertaNo Gravatar February 20th

    Comment Arrow

    thanks for your nice tutorial. But I just wondering why you example works in this way. I got an exception “no class def found” for EhCacheManagerFactoryBean.

    After adding the following dependency to the maven pom it seems to work:

    org.springframework
    spring-context-support
    3.0.0.RELEASE


  2. Eric DalquistNo Gravatar April 15th

    Comment Arrow

    I am one of the authors of a new project intended to provide Ehcache integration for Spring 3 projects via annotations:

    http://code.google.com/p/ehcache-spring-annotations/

    We are excited to announce the general availability of the first production release, 1.0.1.

    This release provides 2 method-level annotations in the spirit of Spring’s @Transactional:

    @Cacheable
    @TriggersRemove

    When appropriately configured in your Spring application, this project will create caching aspects at runtime around your @Cacheable annotated methods.

    Usage documentation can be found on the project wiki:

    http://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheAnnotations
    http://code.google.com/p/ehcache-spring-annotations/wiki/UsingTriggersRemove


  3. VenkatNo Gravatar May 10th

    Comment Arrow

    This is great tutorial. Especially the way the dependencies are explained and the way the dependent xsds are defined is great. Kudos to your time and effort.

    Thanks,
    Venkat


  4. rabbitNo Gravatar September 13th

    Comment Arrow

    Hi, Very nice tutorial.
    Could you help me out in getting the logs for springmodules ?
    Here is my log4j.properties entry :
    log4j.category.org.springmodules=debug, appTest
    log4j.additivity.org.springmodules.cache=false
    log4j.appender.appTest=org.apache.log4j.ConsoleAppender
    log4j.appender.appTest.threshold=DEBUG
    log4j.appender.appTest.layout=org.apache.log4j.PatternLayout
    log4j.appender.appTest.layout.ConversionPattern=%p %d{HH:mm:ss,SSS} %c{2} – %m%n

    I have made the changes as mentioned above. My Junit test runs, but there are no messages from ehcache which come up on the console. So there is no way for me to verify if it is really working.

    Thanks in advance.


  5. xaviNo Gravatar February 21st

    Comment Arrow

    I’ve seen the version of ehcache is 1.6.1 and springmodules 0.9. Do you know which are the updated ones? I’m trying to compile with the information on the ehcache website:

    net.sf.ehcache
    ehcache
    2.3.1
    pom

    But it doesn’t work because there’s some pom that doesn’t has this high version (ehcache-terracotta, on the maven repository the last version is 2.1.1). I can’t even build the project!

    Thanks in advanced!


  6. adeelNo Gravatar October 4th

    Comment Arrow

    Very nice.

    http://eiconsulting.blogspot.com/2011/10/ehcache-implementation-in-spring.html

    Another tutorial with detailed explaination


  7. JoshNo Gravatar November 9th

    Comment Arrow

    Hello, thanks for this great tutorial. How would you integrate transaction management into this example as well?


  8. AnuragNo Gravatar January 11th

    Comment Arrow

    Nice Tutorial sir.. but actually will u provide some more details example form which i can implement in my apps..


Add Yours

  • Author Avatar

    YOU


Comment Arrow




About Author

shiv

This author has not yet written a description. Please give them some time to get acquainted with the site and surely they will write their masterpiece.