Enterprise Java

Simple Spring Memcached – Spring Caching Abstraction and Memcached

Caching remains the one of the most basic performance enhancing mechanism in any read heavy database application. Spring 3.1 release came up with a cool new feature called Cache Abstraction. Spring Cache Abstraction provides the application developers an easy, transparent and decoupled way to implement any caching solution. Memcached is one of the most popular distributed caching system used across apps. In this post we will focus on how to integrate memcached with a Spring enabled applications. Since Spring directly supports only Ehcache and ConcurrentHashMap so we will fall down to a third party library Simple Spring Memcache to leverage power of spring caching abstraction.
 
 

Getting The Code

Code for this tutorial can be downloaded from following SVN location. https://www.assembla.com/code/weblog4j/subversion/nodes/24/SpringDemos/trunk For the tutorial to work please create the following table in your db. Then modify the datasource in springcache.xml.

CREATE  TABLE IF NOT EXISTS `adconnect`.`books` (
  `book_id` INT NOT NULL AUTO_INCREMENT ,
  `book_name` VARCHAR(500) NULL ,
  `book_author` VARCHAR(500) NULL ,
  `category` VARCHAR(500) NULL ,
  `numpages` INT NULL ,
  `price` FLOAT NULL ,
  PRIMARY KEY (`book_id`) )
ENGINE = InnoDB;

Integration Steps

1. Dependencies – I also assume that you have your hibernate, spring and logs set up. So for downloading SSM dependencies add following to your POM. For full set of dependencies please download the project from SVN url above.

<dependency>
     <groupId>com.google.code.simple-spring-memcached</groupId>
     <artifactId>spring-cache</artifactId>
     <version>3.1.0</version>
</dependency>

<dependency>
     <groupId>com.google.code.simple-spring-memcached</groupId>
     <artifactId>xmemcached-provider</artifactId>
     <version>3.1.0</version>
</dependency>

2. Enable Caching – To enable caching in your spring application add following to your spring context xml.

<cache:annotation-driven/>

3. Configure Spring to enable Memcached based caching  – Add following to your application context xml.

<bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager">
     <property name="caches">
         <set>
             <bean class="com.google.code.ssm.spring.SSMCache">
                 <constructor-arg name="cache" index="0" ref="defaultCache"/>
                 <!-- 5 minutes -->
                 <constructor-arg name="expiration" index="1" value="300"/>
                 <!-- @CacheEvict(..., "allEntries" = true) doesn't work -->
                 <constructor-arg name="allowClear" index="2" value="false"/>
             </bean>
         </set>
     </property>

    </bean>

<bean name="defaultCache" class="com.google.code.ssm.CacheFactory">
     <property name="cacheName" value="defaultCache"/>
     <property name="cacheClientFactory">
        <bean name="cacheClientFactory" class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>
     </property>
     <property name="addressProvider">
         <bean class="com.google.code.ssm.config.DefaultAddressProvider">
            <property name="address" value="127.0.0.1:11211"/>
         </bean>
     </property>
     <property name="configuration">
         <bean class="com.google.code.ssm.providers.CacheConfiguration">
             <property name="consistentHashing" value="true"/>
         </bean>
     </property>

</bean>

SSMCacheManager extends org.springframework.cache.support.AbstractCacheManager – It is an  abstract class and is a manager for underlying Cache.

SSMCache implements org.springframework.cache.Cache – This is actual  wrapper round underlying cache client api.

4. Annotation Driven caching – Spring uses annotation to mark a method that it is to be managed by  cache.  These are the annotations defined by spring caching framework

  1. @Cacheable – This annotation is used to mark a method whose results are to be cached. If a cacheable method is called then spring first looks if result of the method is cached or not. If it present in cache then result is pulled from there else it the method call is made.                                                                                                                         
  2. @CachePut – Methods marked with cacheput annotations are always run and their results are pushed to cache. You should not place both Cacheput and Cacheable annotation on same method as they have different behaviour. Cacheput will result in method getting executed all the time while cacheable results in method getting executed only once.
  3. @CacheEvict – This annotation results in eviction of objects from the cache. This is generally used when the result object is updated hence the old object from cache needs to be purged.
  4. @Caching – This annotation is used if multiple annotations of same type is to be put on a method.

@Cacheable Demo 

@Cacheable(value = "defaultCache", key = "new Integer(#book_id).toString().concat('.BookVO')")
    public BookVO get(int book_id) throws Exception {
        BookVO bookVO = null;
		try{
			Query query = getSession().createQuery("from BookVO bookVO where bookVO.book_id=:book_id");
			query.setLong("book_id", book_id);
			bookVO =  (BookVO)query.uniqueResult();
		}catch(HibernateException he){
			log.error("Error in finding a bookVO : " + he);
            throw new Exception("Error in finding adPicVO by book_id for book_id : " + bookVO, he);
		}
		return bookVO;
    }

Please note the key attribute of the annotation. This is an example of Spring Expression Language. You can use SePL use to create memcache key according to your requirement. In this example I want a key which should be of form <book_id>.BookVO. 

Another Example – Lets say I want to store a list of bookVO from a given author in that case I can a unique key of form <author_name>.BookVOList so for that I can use following key

@Cacheable(value = "defaultCache", key = "#author.concat('.BookVOList')")
    public List<BookVO> getList(String author) throws Exception {

@CachePut Demo

@CachePut(value = "defaultCache", key = "new Integer(#bookVO.book_id).toString().concat('.BookVO')")
    public BookVO create(BookVO bookVO) throws Exception {
        try{
			getSession().save(bookVO);
			getSession().flush();
		}catch(HibernateException he){
			log.error("Error in inserting bookVO : " + he);
            throw new Exception("Error in inserting bookVO", he);
		}

		return bookVO;
    }

CachePut can be used while inserting data where data inserted can be put in cache after insertion is done

@CacheEvict Demo

@CacheEvict(value = "defaultCache", key = "new Integer(#bookVO.book_id).toString().concat('.BookVO')")
    public BookVO update(BookVO bookVO) throws Exception {
        try{
            Query query = getSession().createQuery("update BookVO bookVO set bookVO.book_name=:book_name, bookVO.book_author=:book_author,bookVO.category=:category,bookVO.numpages=:numpages,bookVO.price=:price " +
                                                   "where bookVO.book_id=:book_id");
            query.setString("book_name", bookVO.getBook_name());
            query.setString("book_author", bookVO.getBook_author());
            query.setString("category", bookVO.getCategory());
            query.setInteger("numpages", bookVO.getNumpages());
            query.setFloat("price", bookVO.getPrice());
			query.setLong("book_id", bookVO.getBook_id());
            query.executeUpdate();
		}catch(HibernateException he){
			log.error("Error in updating bookVO : " + he);
            throw new Exception("Error in updating bookVO", he);
		}

		return bookVO;
    }

Resources

  1. https://code.google.com/p/simple-spring-memcached/
  2. http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/cache.html
  3. http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/expressions.html
  4. http://static.springsource.org/spring/docs/3.1.0.M1/javadoc-api/index.html?org/springframework/cache/CacheManager.html
  5. http://doanduyhai.wordpress.com/2012/07/01/cache-abstraction-in-spring-3/
  6. http://viralpatel.net/blogs/cache-support-spring-3-1-m1/

 

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Veda
Veda
9 years ago

I noticed if there are multiple @Cacheable methods are specified in a class and if they are called from the same class, then only one of the method is cacheable; others are not.

Work around: Move the ‘Cacheable’ methods to a different class and every other class calls this new ‘Cacheable’ class methods.
Reason: The proxy class (the default ‘mode’ is ‘proxy’) created by Spring works only for external classes and not a method within the same class!

Hope this helps others, since it took more than a day to figure out the problem!

abhijit
8 years ago

I implement this but it is not working.I check all keys in memcached using command stats items.Please help me.

Back to top button