Caching with Spring Data Redis

In the example below, I’ll show you how to use the Spring Data – Redis project as a caching provider for the Spring Cache Abstraction that was introduced in Spring 3.1. I get a lot of questions about how to use Spring’s Java based configuration so I’ll provide both XML and Java based configurations for your review.

Dependencies

The following dependencies were used in this example:

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.joshuawhite.example</groupId>
    <artifactId>spring-redis-example</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>Spring Redis Example</name>
    <dependencies>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.0.2.RELEASE</version>
        </dependency>       
        <!-- required for @Configuration annotation -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.0.0</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Code and Configuration

The HelloService example below is very simple. As you will see in the implementation, it simply returns a String with “Hello” prepended to the name that is passed in.

HelloService.java
1
2
3
4
5
6
7
package com.joshuawhite.example.service;
 
public interface HelloService {
 
    String getMessage(String name);
 
}

Looking at the HelloServiceImpl class (below), you can see that I am leveraging Spring’s @Cacheable annotation to add caching capabilities to the getMessage method. For more details on the capabilities of this annotation, take a look at theCache Abstraction documentation.  For fun, I am using the Spring Expression Language (SpEL) to define a condition. In this example, the methods response will only be cached when the name passed in is “Joshua”.

HelloServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.joshuawhite.example.service;
 
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
 
@Service("helloService")
public class HelloServiceImpl implements HelloService {
 
    /**
     * Using SpEL for conditional caching - only cache method executions when
     * the name is equal to "Joshua"
     */
    @Cacheable(value="messageCache", condition="'Joshua'.equals(#name)")
    public String getMessage(String name) {
        System.out.println("Executing HelloServiceImpl" +
                        ".getHelloMessage(\"" + name + "\")");
 
        return "Hello " + name + "!";
    }
 
}

The App class below contains our main method and is used to select between XML and Java based configurations. Each of the System.out.println‘s are used to demonstrate when caching is taking place. As a reminder, we only expect method executions passing in “Joshua” to be cached. This will be more clear when we look at the programs output later.

App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.joshuawhite.example;
 
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
 
import com.joshuawhite.example.config.AppConfig;
import com.joshuawhite.example.service.HelloService;
 
public class App {
 
    public static void main(String[] args) {
 
        boolean useJavaConfig  = true;
        ApplicationContext ctx = null;
 
        //Showing examples of both Xml and Java based configuration
        if (useJavaConfig ) {
                ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        }
        else {
                ctx = new GenericXmlApplicationContext("/META-INF/spring/app-context.xml");
        }
 
        HelloService helloService = ctx.getBean("helloService", HelloService.class);
 
        //First method execution using key="Josh", not cached
        System.out.println("message: " + helloService.getMessage("Josh"));
 
        //Second method execution using key="Josh", still not cached
        System.out.println("message: " + helloService.getMessage("Josh"));
 
        //First method execution using key="Joshua", not cached
        System.out.println("message: " + helloService.getMessage("Joshua"));
 
        //Second method execution using key="Joshua", cached
        System.out.println("message: " + helloService.getMessage("Joshua"));
 
        System.out.println("Done.");
    }
 
}

Notice that component scanning is still used when using the XML based configuration. You can see that I am using the@Service annotation on line 6 of HelloServiceImpl.java above.

Next we will take a look at how to configure a jedisConnectionFactoryredisTemplate and cacheManager.

Configuring the JedisConnectionFactory

For this example, I chose to use Jedis as our Java client of choice because it is listed on the Redis site as being the“recommended” client library for Java. As you can see, the setup is very straight forward. While I am explicitly setting use-pool=true, it the source code indicates that this is the default. The JedisConnectionFactory also provides the following defaults when not explicitly set:

  • hostName=”localhost”
  • port=6379
  • timeout=2000 ms
  • database=0
  • usePool=true
Note: Though the database index is configurable, the JedisConnectionFactory only supports connecting to one Redis database at a time. Because Redis is single threaded, you are encouraged to set up multiple instances of Redis instead of using multiple databases within a single process. This allows you to get better CPU/resource utilization. If you plan to use redis-cluster, only a single database is supported.

For more information about the defaults used in the connection pool, take a look at the implementation of JedisPoolConfigor the Apache Commons Pool org.apache.commons.pool.impl.GenericObjectPool.Config and it’s enclosingorg.apache.commons.pool.impl.GenericObjectPool class.

Configuring the RedisTemplate

As you would expect from a Spring “template” class, the RedisTemplate takes care of serialization and connection management and (providing you are using a connection pool) is thread safe.

By default, the RedisTemplate uses Java serialization (JdkSerializationRedisSerializer). Note that serializing data into Redis essentially makes Redis an “opaque” cache. While other serializers allow you to map the data into Redis, I have found serialization, especially when dealing with object graphs, is faster and simpler to use. That being said, if you have a requirement that other non-java applications be able to access this data, mapping is your best out-of-the-box option.

I have had a great experience using Hessian and Google Protocol Buffers/protostuff. I’ll share some sample implementations of the RedisSerializer in a future post.

Configuring the RedisCacheManager

Configuring the RedisCacheManager is straight forward. As a reminder, the RedisCacheManager is dependent on aRedisTemplate which is dependent on a connection factory, in our case JedisConnectionFactory, that can only connect to a single database at a time.

As a workaround, the RedisCacheManager has the capability of setting up a prefix for your cache keys.

Warning: When dealing with other caching solutions, Spring’s CacheManger usually contains a map of Cache(each implementing map like functionality) implementations that are backed by separate caches. Using the defaultRedisCacheManager configuration, this is not the case. Based on the javadoc comment on the RedisCacheManager, its not clear if this is a bug or simply incomplete documentation.

“…By default saves the keys by appending a prefix (which acts as a namespace).”

While the DefaultRedisCachePrefix which is configured in the RedisCacheManager certainly supports this, it is not enabled by default. As a result, when you ask the RedisCacheManager for a Cache of a given name, it simply creates a new Cache instance that points to the same database. As a result, the Cache instances are all the same. The same key will retrieve the same value in all Cache instances.

As the javadoc comment alludes to, prefixs can be used to setup client managed (Redis doesn’t support this functionality natively) namespaces that essentially create “virtual” caches within the same database. You can turn this feature on by calling redisCacheManager.setUsePrefix(true) either using the Spring XML or Java configuration.

app-context.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?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:context="http://www.springframework.org/schema/context"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
 
http://www.springframework.org/schema/beansvhttp://www.springframework.org/schema/beans/spring-beans.xsd
 
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
 
    <context:component-scan base-package="com.joshuawhite.example.service" />
    <context:property-placeholder location="classpath:/redis.properties"/>
 
    <!-- turn on declarative caching -->
    <cache:annotation-driven />
 
    <!-- Jedis ConnectionFactory -->
    <bean
        id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
        p:host-name="${redis.host-name}"
        p:port="${redis.port}"
        p:use-pool="true"/>
 
    <!-- redis template definition -->
    <bean
        id="redisTemplate"
        class="org.springframework.data.redis.core.RedisTemplate"
        p:connection-factory-ref="jedisConnectionFactory"/>
 
    <!-- declare Redis Cache Manager -->
    <bean
        id="cacheManager"
        class="org.springframework.data.redis.cache.RedisCacheManager"
        c:template-ref="redisTemplate"/>
 
</beans>

The Java configuration below is equivalent to the XML configuration above. People usually get hung up on using aPropertySourcesPlaceholderConfigurer. To do that, you need to use both the @PropertySource annotation and define aPropertySourcesPlaceholderConfigurer bean. The PropertySourcesPlaceholderConfigurer will not be sufficient on its own.

AppConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.joshuawhite.example.config;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
 
@Configuration
@EnableCaching
@ComponentScan("com.joshuawhite.example")
@PropertySource("classpath:/redis.properties")
public class AppConfig {
 
 private @Value("${redis.host-name}") String redisHostName;
 private @Value("${redis.port}") int redisPort;
 
 @Bean
 public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
     return new PropertySourcesPlaceholderConfigurer();
 }
 
 @Bean
 JedisConnectionFactory jedisConnectionFactory() {
     JedisConnectionFactory factory = new JedisConnectionFactory();
     factory.setHostName(redisHostName);
     factory.setPort(redisPort);
     factory.setUsePool(true);
     return factory;
 }
 
 @Bean
 RedisTemplate<Object, Object> redisTemplate() {
     RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
     redisTemplate.setConnectionFactory(jedisConnectionFactory());
     return redisTemplate;
 }
 
 @Bean
 CacheManager cacheManager() {
     return new RedisCacheManager(redisTemplate());
 }
 
}

Here is the properties file that is used by both configurations. Replace the values below with the host and port that you are using.

redis.properties
1
2
redis.host-name=yourHostNameHere
redis.port=6379

Output

Finally, here is the output from our brief example application. Notice that no matter how many times we callgetHelloMessage("Josh"), the methods response does not get cached. This is because we defined a condition (seeHelloServiceImpl.java, line 13) where we only cache the methods response when the name equals “Joshua”.

When we call getHelloMessage("Joshua") for the first time, the method is executed. The second time however, it is not.

Output
1
2
3
4
5
6
7
8
Executing HelloServiceImpl.getHelloMessage("Josh")
message: Hello Josh!
Executing HelloServiceImpl.getHelloMessage("Josh")
message: Hello Josh!
Executing HelloServiceImpl.getHelloMessage("Joshua")
message: Hello Joshua!
message: Hello Joshua!
Done.

This concludes our brief over view of caching with Spring Data Redis.



source - http://blog.joshuawhite.com/java/caching-with-spring-data-redis/







03.23.2012

This article will cover the following topics:


  1. How to install Redis on a *nix machine.
  2. How to write a simple app using Spring-Data and Redis.


Before moving forward let's understand what Redis is.

Redis is an open source, advanced key-value store. It is often referred to as a data structure serversince keys can contain stringshasheslistssets and sorted sets.

In this article I'm not going to talk how fast is redis and how it works. But believe me it's a very fast key-value store.

Now let's install redis on *nix machine

2.tar xzf redis-2.4.8.tar.gz
3.cd redis-2.4.8
4.make
5. 
6.src/redis-server

That's all! Your redis is ready to use.

Now let's create a simple maven application to work with redis.

 Currently Spring Redis has support for Jedis and JRedis (Jedis and JRedis are Redis bindings for Java).

 In this article we are going to use Jedis. Here are the Maven dependencies:

01.<!-- Redis Java Binding-->
02.<dependency>
03.<groupId>redis.clients</groupId>
04.<artifactId>jedis</artifactId>
05.<version>2.0.0</version>
06.</dependency>
07. 
08.<!-- Spring Data Redis -->
09.<dependency>
10.<groupId>org.springframework.data</groupId>
11.<artifactId>spring-data-redis</artifactId>
12.<version>1.0.0.RELEASE</version>
13.</dependency>

You can find the full application on GitHub here: spring-data-redis example

Now let's define and configure our beans:

01.<!-- redis conection factory -->
02.<bean id="connectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
03.p:use-pool="true" p:host-name="${app.config.redis.host}"p:port="${app.config.redis.port}"p:password="${app.config.redis.password}"/>
04. 
05.<!-- redis template -->
06.<bean id="redisTemplate"class="org.springframework.data.redis.core.RedisTemplate"
07.p:connection-factory-ref="connectionFactory"/>
08. 
09.<!-- redis string template -->
10.<bean id="redisStringTemplate"class="org.springframework.data.redis.core.StringRedisTemplate"
11.p:connection-factory-ref="connectionFactory"/>
12. 
13.<!-- redis map binding -->
14.<bean id="userRedisMap"class="org.springframework.data.redis.support.collections.DefaultRedisMap">
15.<constructor-arg ref="redisTemplate"/>
16.<constructor-arg value="USER"/>
17.</bean>

RedisTemplate provides high level abstraction for redis operations. If your application stores and retrieves strings, then you may use StringRedisTemplate.

Let's create a simple domain object.

01.public interface Cachable extends Serializable {
02. 
03.public String getKey();
04. 
05.public String getObjectKey();
06.}
07. 
08.public class User implements Cachable {
09. 
10.private static final long serialVersionUID = -7898194272883238670L;
11. 
12.public static final String OBJECT_KEY = "USER";
13. 
14.public User() {
15.}
16. 
17.public User(String id) {
18.}
19. 
20.public User(String id, String name) {
21.this.id = id;
22.this.name = name;
23.}
24. 
25.private String id;
26. 
27.private String name;
28. 
29.public String getId() {
30.return id;
31.}
32. 
33.public void setId(String id) {
34.this.id = id;
35.}
36. 
37.public String getName() {
38.return name;
39.}
40. 
41.public void setName(String name) {
42.this.name = name;
43.}
44. 
45.@Override
46.public String toString() {
47.return "User [id=" + id + ", name=" + name + "]";
48.}
49. 
50.@Override
51.public String getKey() {
52.return getId();
53.}
54. 
55.@Override
56.public String getObjectKey() {
57.return OBJECT_KEY;
58.}
59.}

Now let's create Service layer classes.

01.public interface Service<V extends Cachable> {
02. 
03.public void put(V obj);
04. 
05.public V get(V key);
06. 
07.public void delete(V key);
08.}
09. 
10.@Service("userService")
11.public class UserService implements co.sdr.service.Service<User> {
12. 
13.@Autowired
14.RedisTemplate<String, Cachable> redisTemplate;
15. 
16.@Override
17.public void put(User user) {
18.redisTemplate.opsForHash().put(user.getObjectKey(), user.getKey(), user);
19.}
20. 
21.@Override
22.public void delete(User key) {
23.redisTemplate.opsForHash().delete(key.getObjectKey(), key.getKey());
24.}
25. 
26.@Override
27.public User get(User key) {
28.return (User) redisTemplate.opsForHash().get(key.getObjectKey(), key.getKey());
29.}
30.}

So domain and service classes are ready. Now let's use them.

Here is the simple Main class which uses the above-mentioned classes.

01.public class Main
02.{
03.public static void main( String[] args )
04.{
05.ApplicationContext context = newClassPathXmlApplicationContext("springapp.xml");
06. 
07.@SuppressWarnings("unchecked")
08.Service<User> userService = (Service<User>)context.getBean("userService");
09. 
10.User user1 = new User("user1ID""User 1");
11.User user2 = new User("user2ID""User 2");
12. 
13.System.out.println("==== getting objects from redis ====");
14.System.out.println("User is not in redis yet: " + userService.get(user1));
15.System.out.println("User is not in redis yet: " + userService.get(user2));
16. 
17.System.out.println("==== putting objects into redis ====");
18.userService.put(user1);
19.userService.put(user2);
20. 
21.System.out.println("==== getting objects from redis ====");
22.System.out.println("User should be in redis yet: " + userService.get(user1));
23.System.out.println("User should be in redis yet: " + userService.get(user2));
24. 
25.System.out.println("==== deleting objects from redis ====");
26.userService.delete(user1);
27.userService.delete(user2);
28. 
29.System.out.println("==== getting objects from redis ====");
30.System.out.println("User is not in redis yet: " + userService.get(user1));
31.System.out.println("User is not in redis yet: " + userService.get(user2));
32. 
33.}
34.}

Wasn't it simple? Spring makes everything much more easy to use.

And this isn't the end. spring-data-redis's org.springframework.data.redis.support package provides List, Set, Map implemetations.

Let's try to use a map:

01.@Service("userMapService")
02.public class UserMapService implements co.sdr.service.Service<User> {
03. 
04.@Autowired
05.Map<String, Cachable> userRedisMap;
06. 
07.@Override
08.public void put(User user) {
09.userRedisMap.put(user.getKey(), user);
10.}
11. 
12.@Override
13.public void delete(User key) {
14.userRedisMap.remove(key.getKey());
15.}
16. 
17.@Override
18.public User get(User key) {
19.return (User) userRedisMap.get(key.getKey());
20.}
21.}

This may help you with unit testing, and you may change the datastore without the changing service layer.

You may also use RedisCacheManager as a backing implementation for spring cache abstraction.

Find the full version of this application on GitHub here:  spring-data-redis example



source - http://java.dzone.com/articles/spring-data-redis-0








2013년 3월 26일 화요일

Spring 3.1 + Redis 를 이용한 Cache

이번 주에는 Spring 3.1 에서 지원하는 Cache 관련해서 많은 글을 썼는데, 요즘 가장 많이 사용되는 Redis 를 저장소로 사용하는 Cache 를 만들겠습니다.

Redis 를 구현하기 위해서 spring-data-redis 와 jedis 라이브러리를 사용했습니다.
jedis 만으로도 구현할 수 있지만, 편하게 spring-data-redis 의 RedisTemplate 를 사용하기로 했습니다.

우선 Redis 를 캐시 저장소로 사용하기 위해 환경설정을 합니다.

1. RedisCacheConfiguration.java

12345678910111213141516171819202122232425262728293031
@Configuration
@EnableCaching
@ComponentScan(basePackageClasses = UserRepository.class)
// @PropertySource("classpath:redis.properties")
public class RedisCacheConfiguration {
 
@Autowired
Environment env;
 
@Bean
public JedisShardInfo jedisShardInfo() {
return new JedisShardInfo("localhost");
}
 
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new JedisConnectionFactory(jedisShardInfo());
}
 
@Bean
public RedisTemplate redisTemplate() {
RedisTemplate<String,Object> template = new RedisTemplate<String,Object>();
template.setConnectionFactory(redisConnectionFactory());
return template;
}
 
@Bean
public RedisCacheManager redisCacheManager() {
return new RedisCacheManager(redisTemplate(), 300);
}
}

한가지 RedisCacheFactory를 생성할 때 주의할 점은 JedisShardInfo 로 생성해야지, JedisPoolingConfig나 기본 생성자로 생성 시에 RedisTemplate 에서 connection을 제대로 생성 못하는 버그가 있더군요 ㅠ.ㅠ 이 것 때문에 반나절을 허비...

2. RedisCacheManager.java

1234567891011121314151617181920212223242526272829303132333435363738
@Slf4j
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
 
private RedisTemplate redisTemplate;
private int expireSeconds;
 
public RedisCacheManager(RedisTemplate redisTemplate) {
this(redisTemplate, 300);
}
 
public RedisCacheManager(RedisTemplate redisTemplate, int expireSeconds) {
Guard.shouldNotBeNull(redisTemplate, "redisTemplate");
this.redisTemplate = redisTemplate;
this.expireSeconds = expireSeconds;
}
 
@Override
protected Collection<? extends Cache> loadCaches() {
Collection<Cache> caches = Lists.newArrayList();
 
for (String name : getCacheNames()) {
caches.add(new RedisCache(name, redisTemplate, expireSeconds));
}
return caches;
}
 
@Override
public Cache getCache(String name) {
synchronized (this) {
Cache cache = super.getCache(name);
if (cache == null) {
cache = new RedisCache(name, redisTemplate, expireSeconds);
addCache(cache);
}
return cache;
}
}
}


 3. RedisCache.java

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
@Slf4j
public class RedisCache implements Cache {
@Getter
private String name;
@Getter
private int expireSeconds;
 
private RedisTemplate redisTemplate;
 
public RedisCache(String name, RedisTemplate redisTemplate) {
this(name, redisTemplate, 300);
}
 
public RedisCache(String name, RedisTemplate redisTemplate, int expireSeconds) {
Guard.shouldNotBeEmpty(name, "name");
Guard.shouldNotBeNull(redisTemplate, "redisTemplate");
 
this.name = name;
this.redisTemplate = redisTemplate;
 
if (log.isDebugEnabled())
log.debug("MongoCache를 생성합니다. name=[{}], mongodb=[{}]", name, redisTemplate);
}
 
@Override
public Object getNativeCache() {
return redisTemplate;
}
 
public String getKey(Object key) {
return name + ":" + key;
}
 
@Override
public ValueWrapper get(Object key) {
Guard.shouldNotBeNull(key, "key");
if (log.isDebugEnabled())
log.debug("캐시 키[{}] 값을 구합니다...", key);
 
Object result = redisTemplate.opsForValue().get(getKey(key));
 
SimpleValueWrapper wrapper = null;
if (result != null) {
if (log.isDebugEnabled())
log.debug("캐시 값을 로드했습니다. key=[{}]", key);
wrapper = new SimpleValueWrapper(result);
}
return wrapper;
}
 
@Override
@SuppressWarnings("unchecked")
public void put(Object key, Object value) {
Guard.shouldNotBeNull(key, "key");
 
if (log.isDebugEnabled())
log.debug("캐시에 값을 저장합니다. key=[{}], value=[{}]", key, value);
 
redisTemplate.opsForValue().set(getKey(key), value, expireSeconds);
}
 
@Override
@SuppressWarnings("unchecked")
public void evict(Object key) {
Guard.shouldNotBeNull(key, "key");
if (log.isDebugEnabled())
log.debug("지정한 키[{}]의 캐시를 삭제합니다...", key);
 
try {
redisTemplate.delete(key);
} catch (Exception e) {
log.error("캐시 항목 삭제에 실패했습니다. key=" + key, e);
}
}
 
@Override
@SuppressWarnings("unchecked")
public void clear() {
if (log.isDebugEnabled())
log.debug("모든 캐시를 삭제합니다...");
try {
redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushAll();
return null;
}
});
} catch (Exception e) {
log.warn("모든 캐시를 삭제하는데 실패했습니다.", e);
}
}
}
view rawRedisCache.java hosted with ❤ by GitHub


RedisCache의 get / put 은 일반적으로 쓰는 opsForValue() 를 사용했습니다. 다른 것을 사용할 수도 있을텐데, 좀 더 공부한 다음에 다른 것으로 변경해 봐야 할 듯 합니다.
마지막에 clear() 메소드도 jedis 에는 flushDB(), flushAll() 메소드를 지원하는데, RedisTemplate에서는 해당 메소드를 expose 하지 않아 코드와 같이 RedisCallback 을 구현했습니다.

4. RedisCacheTest.java

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RedisCacheConfiguration.class})
public class RedisCacheTest {
 
@Autowired
RedisCacheManager redisCacheManager;
 
@Autowired
UserRepository userRepository;
 
@Test
public void clearTest() {
Assert.assertNotNull(redisCacheManager);
Cache cache = redisCacheManager.getCache("user");
Assert.assertNotNull(cache);
cache.clear();
Assert.assertNotNull(cache);
}
 
@Test
public void getUserFromCache() {
 
Stopwatch sw = new Stopwatch("initial User");
sw.start();
User user1 = userRepository.getUser("debop", 100);
sw.stop();
 
sw = new Stopwatch("from Cache");
sw.start();
User user2 = userRepository.getUser("debop", 100);
sw.stop();
 
Assert.assertEquals(user1.getUsername(), user2.getUsername());
}
 
@Test
public void componentConfigurationTest() {
Assert.assertNotNull(redisCacheManager);
Cache cache = redisCacheManager.getCache("user");
Assert.assertNotNull(cache);
 
cache.evict("debop");
 
Assert.assertNotNull(userRepository);
}
}
view rawRedisCacheTest.java hosted with ❤ by GitHub


UserRepository는 전에 쓴 Spring 3.1 + EhCache 등의 글과 같은 코드라 생략했습니다.
Redis 관련은 Windows 에서는 구 버전만 지원하고, 신 버전은 linux 만 가능하더군요...
그래도 성능은 정평이 나있으니, HA 구성 시에는 가장 먼저 고려되어야 할 캐시 저장소라 생각됩니다.

저는 앞으로 hibernate  2nd cache provider for redis,  hibernate-ogm-redis 를 만들어 볼 예정입니다.



출처 - http://debop.blogspot.kr/2013/03/spring-31-redis-cache.html








Using Redis with Spring

As NoSQL solutions are getting more and more popular for many kind of problems, more often the modern projects consider to use some (or several) of NoSQLs instead (or side-by-side) of traditional RDBMS. I have already covered my experience with MongoDB in thisthis and this posts. In this post I would like to switch gears a bit towards Redis, an advanced key-value store.
 
Aside from very rich key-value semantics, Redis also supports pub-sub messaging and transactions. In this post I am going just to touch the surface and demonstrate how simple it is to integrate Redis into your Spring application. As always, we will start with Maven POM file for our project:

 

02  
03  
04 <modelversion>4.0.0</modelversion>
05 <groupid>com.example.spring</groupid>
06 <artifactid>redis</artifactid>
07 <version>0.0.1-SNAPSHOT</version>
08 <packaging>jar</packaging>
09 
10 <properties>
11  <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
12  <spring.version>3.1.0.RELEASE</spring.version>
13 </properties>
14 
15 <dependencies>
16  <dependency>
17   <groupid>org.springframework.data</groupid>
18   <artifactid>spring-data-redis</artifactid>
19   <version>1.0.0.RELEASE</version>
20  </dependency>
21 
22  <dependency>
23   <groupid>cglib</groupid>
24   <artifactid>cglib-nodep</artifactid>
25   <version>2.2</version>
26  </dependency>
27 
28  <dependency>
29   <groupid>log4j</groupid>
30   <artifactid>log4j</artifactid>
31   <version>1.2.16</version>
32  </dependency>
33 
34  <dependency>
35   <groupid>redis.clients</groupid>
36   <artifactid>jedis</artifactid>
37   <version>2.0.0</version>
38   <type>jar</type>
39  </dependency>
40 
41  <dependency>
42   <groupid>org.springframework</groupid>
43   <artifactid>spring-core</artifactid>
44   <version>${spring.version}</version>
45  </dependency>
46 
47  <dependency>
48   <groupid>org.springframework</groupid>
49   <artifactid>spring-context</artifactid>
50   <version>${spring.version}</version>
51  </dependency>
52 </dependencies>
53</project>
Spring Data Redis is the another project under Spring Data umbrella which provides seamless injection of Redis into your application. The are several Redis clients for Java and I have chosen the Jedis as it is stable and recommended by Redis team at the moment of writing this post.
We will start with simple configuration and introduce the necessary components first. Then as we move forward, the configuration will be extended a bit to demonstrated pub-sub capabilities. Thanks to Java config support, we will create the configuration class and have all our dependencies strongly typed, no XML anymore:
01package com.example.redis.config;
02 
03import org.springframework.context.annotation.Bean;
04import org.springframework.context.annotation.Configuration;
05import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
06import org.springframework.data.redis.core.RedisTemplate;
07import org.springframework.data.redis.serializer.GenericToStringSerializer;
08import org.springframework.data.redis.serializer.StringRedisSerializer;
09 
10@Configuration
11public class AppConfig {
12 @Bean
13 JedisConnectionFactory jedisConnectionFactory() {
14  return new JedisConnectionFactory();
15 }
16 
17 @Bean
18 RedisTemplate< String, Object > redisTemplate() {
19  final RedisTemplate< String, Object > template =  new RedisTemplate< String, Object >();
20  template.setConnectionFactory( jedisConnectionFactory() );
21  template.setKeySerializer( new StringRedisSerializer() );
22  template.setHashValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
23  template.setValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
24  return template;
25 }
26}
That’s basically everything we need assuming we have single Redis server up and running on localhost with default configuration. Let’s consider several common uses cases: setting a key to some value, storing the object and, finally, pub-sub implementation. Storing and retrieving a key/value pair is very simple:
1@Autowired private RedisTemplate< String, Object > template;
2 
3public Object getValue( final String key ) {
4    return template.opsForValue().get( key );
5}
6 
7public void setValue( final String key, final String value ) {
8    template.opsForValue().set( key, value );
9}

Optionally, the key could be set to expire (yet another useful feature of Redis), f.e. let our keys expire in 1 second:

1public void setValue( final String key, final String value ) {
2    template.opsForValue().set( key, value );
3    template.expire( key, 1, TimeUnit.SECONDS );
4}

Arbitrary objects could be saved into Redis as hashes (maps), f.e. let save instance of some class User

1public class User {
2 private final Long id;
3 private String name;
4 private String email;
5        
6    // Setters and getters are omitted for simplicity
7}

into Redis using key pattern “user:<id>”:

01public void setUser( final User user ) {
02 final String key = String.format( "user:%s", user.getId() );
03 final Map< String, Object > properties = new HashMap< String, Object >();
04 
05 properties.put( "id", user.getId() );
06 properties.put( "name", user.getName() );
07 properties.put( "email", user.getEmail() );
08 
09 template.opsForHash().putAll( key, properties);
10}

Respectively, object could easily be inspected and retrieved using the id.

1public User getUser( final Long id ) {
2 final String key = String.format( "user:%s", id );
3 
4 final String name = ( String )template.opsForHash().get( key, "name" );
5 final String email = ( String )template.opsForHash().get( key, "email" );
6 
7 return new User( id, name, email );
8}
There are much, much more which could be done using Redis, I highly encourage to take a look on it. It surely is not a silver bullet but could solve many challenging problems very easy. Finally, let me show how to use a pub-sub messaging with Redis. Let’s add a bit more configuration here (as part of AppConfig class):
01@Bean
02MessageListenerAdapter messageListener() {
03 return new MessageListenerAdapter( new RedisMessageListener() );
04}
05 
06@Bean
07RedisMessageListenerContainer redisContainer() {
08 final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
09 
10 container.setConnectionFactory( jedisConnectionFactory() );
11 container.addMessageListener( messageListener(), new ChannelTopic( "my-queue" ) );
12 
13 return container;
14}
The style of message listener definition should look very familiar to Spring users: generally, the same approach we follow to define JMS message listeners. The missed piece is our RedisMessageListener class definition:
01package com.example.redis.impl;
02 
03import org.springframework.data.redis.connection.Message;
04import org.springframework.data.redis.connection.MessageListener;
05 
06public class RedisMessageListener implements MessageListener {
07 @Override
08 public void onMessage(Message message, byte[] paramArrayOfByte) {
09  System.out.println( "Received by RedisMessageListener: " + message.toString() );
10 }
11}
Now, when we have our message listener, let see how we could push some messages into the queue using Redis. As always, it’s pretty simple:
01@Autowired private RedisTemplate< String, Object > template;
02 
03public void publish( final String message ) {
04 template.execute(
05  new RedisCallback< Long >() {
06   @SuppressWarnings"unchecked" )
07   @Override
08   public Long doInRedis( RedisConnection connection ) throws DataAccessException {
09    return connection.publish(
10     ( ( RedisSerializer< String > )template.getKeySerializer() ).serialize( "queue" ),
11     ( ( RedisSerializer< Object > )template.getValueSerializer() ).serialize( message ) );
12   }
13  }
14 );
15}

That’s basically it for very quick introduction but definitely enough to fall in love with Redis.

Reference: Using Redis with Spring from our JCG partner Andrey Redko at the Andriy Redko {devmind} blog.



source - http://www.javacodegeeks.com/2012/06/using-redis-with-spring.html








As you’ve read about Redis Installation & Configuration, you’re mostly understand the main concept of Redis database. Redis is an extremely high-performed, lightweight data store. It provides key/value data access to persistent byte arrays, lists, sets and hash data structures. So, the insertion, deletion and retrieve operations inside Redis becomes very simple how we have worked with using of Map, List , Set and an Array data structures how it is used inside normal Java program; although some minimal modifications that you’ve probably never care about them.

As you’ve been doing once it comes to deal with any database platform, you have to download/install the database driver. For Redis four driver libraries are provided for allowing your application to get connected to Redis. Spring Data supports connecting to Redis using either the Jedis (That will be considered in this tutorial), JRedisRJC or SRP driver libraries. It doesn’t matter which library have been used, cause the Spring Data has abstracted the differences between those drivers into common set of APIs and template-style helpers.

1. Spring Context Configuration

To connect to Redis using Jedis, you need to create an instance of org.springframework.data.redis.connection.jedis.JedisConnectionFactory. The other driver libraries have corresponding ConnectionFactory subclasses. Below the proper Spring Context (SpringContext.xml) that should be used for initializing and connecting the Redis database through using of Spring.

SpringContext.xml

1<?xml version="1.0" encoding="UTF-8"?>
10 
11    <!--  Scanning the Spring Beans -->
12    <context:component-scan base-package="net.javabeat.springdata.beans"></context:component-scan>
13    <!-- Redis Connection Factory -->
14    <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
15            p:use-pool="true" />
16    <!-- Redis Template -->
17    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
18            p:connection-factory-ref="jedisConnFactory" />
19 
20</beans>
  • For searching about all Spring component (beans), you’ve provided the component-scan element.
  • For connecting the Redis database, you’ve provided the Jedis connection factory bean.
  • The central abstraction you’re likely to use when accessing Redis via Spring Data Redis is the org.springframework.data.redis.core.RedisTemplate class.

Since the feature set of Redis is really too large to effectively encapsulate into a single class, the various operations on data are split into separate operations classes as follows:

  • ValueOperations:Returns the operations performed on simple values (or Strings in Redis terminology.
  • ListOperations:Returns the operations performed on list values.
  • SetOperations:Returns the operations performed on set values.
  • ZSetOperations:Returns the operations performed on zset values (also known as sorted sets.
  • HashOperations:Returns the operations performed on hash values.
  • BoundValueOperations:Returns the operations performed on hash values bound to the given key.
  • BoundListOperations:Returns the operations performed on list values bound to the given key.
  • BoundSetOperations:Returns the operations performed on set values bound to the given key.
  • BoundZSetoperations:Returns the operations performed on zset values (also known as sorted sets) bound to the given key.
  • BoundHashOperations:Returns the operations performed on hash values bound to the given key.

2. Maven Dependencies for Spring Data Redis

The required libraries and dependencies for making the connecting of Redis is achievable are listed inside the below pom.xml file.

pom.xml

1<?xml version="1.0" encoding="UTF-8"?>
4    <modelVersion>4.0.0</modelVersion>
5    <groupId>net.javabeat.springdata</groupId>
6    <artifactId>SpringData</artifactId>
7    <version>1.0</version>
8    <packaging>jar</packaging>
9    <name>Spring Data</name>
10    <build>
11        <plugins>
12            <plugin>
13                <groupId>org.apache.maven.plugins</groupId>
14                <artifactId>maven-compiler-plugin</artifactId>
15                <version>3.1</version>
16                <configuration>
17                    <source>1.6</source>
18                    <target>1.6</target>
19                    <encoding>UTF-8</encoding>
20                </configuration>
21            </plugin>
22        </plugins>
23    </build>
24    <dependencies>
25        <!-- SLF4J dependency -->
26        <dependency>
27            <groupId>org.slf4j</groupId>
28            <artifactId>slf4j-log4j12</artifactId>
29            <version>1.6.1</version>
30        </dependency>
31        <!-- Spring Data - Redis Library -->
32        <dependency>
33            <groupId>org.springframework.data</groupId>
34            <artifactId>spring-data-redis</artifactId>
35            <version>1.2.1.RELEASE</version>
36        </dependency>
37        <!-- Jedis Driver Library -->
38        <dependency>
39            <groupId>redis.clients</groupId>
40            <artifactId>jedis</artifactId>
41            <version>2.4.2</version>
42        </dependency>
43        <!-- Spring Core -->
44        <dependency>
45            <groupId>org.springframework</groupId>
46            <artifactId>spring-core</artifactId>
47            <version>4.0.0.RELEASE</version>
48        </dependency>
49        <!-- Spring Data commons -->
50        <dependency>
51            <groupId>org.springframework.data</groupId>
52            <artifactId>spring-data-commons</artifactId>
53            <version>1.5.0.RELEASE</version>
54        </dependency>
55        <dependency>
56            <groupId>org.springframework</groupId>
57            <artifactId>spring-core</artifactId>
58            <version>4.0.3.RELEASE</version>
59        </dependency>
60        <dependency>
61            <groupId>org.apache.commons</groupId>
62            <artifactId>commons-pool2</artifactId>
63            <version>2.2</version>
64        </dependency>
65        <dependency>
66            <groupId>org.springframework</groupId>
67            <artifactId>spring-beans</artifactId>
68            <version>4.0.3.RELEASE</version>
69        </dependency>
70    </dependencies>
71</project>

3. Spring Beans (RegisterationBean)

For accessing the RedisTemplate that defined above, you have to use @Autowired annotation for being RedisTemplate available at your Spring Beans.

RegistrationBean.java

1package net.javabeat.springdata.beans;
2 
3import net.javabeat.springdata.jpa.data.User;
4 
5import org.springframework.beans.factory.annotation.Autowired;
6import org.springframework.data.redis.core.RedisTemplate;
7import org.springframework.stereotype.Component;
8 
9@Component
10public class RegistrationBean {
11    @Autowired
12    private RedisTemplate<String,User> redisTemplate;
13 
14    public RedisTemplate<String, User> getRedisTemplate() {
15        return redisTemplate;
16    }
17 
18    public void setRedisTemplate(RedisTemplate<String, User> redisTemplate) {
19        this.redisTemplate = redisTemplate;
20    }
21}

4. Java Beans

The business domain is very simple, it’s just a User entity associated with an instance of Address. Look below.

User.java

1package net.javabeat.springdata.jpa.data;
2 
3import java.io.Serializable;
4 
5public class User implements Serializable{
6 
7    private static final long serialVersionUID = 1L;
8 
9    private String id;
10 
11    private String fullName;
12 
13    private String age;
14 
15    private String status;
16 
17    private Address address;
18 
19    public String getFullName() {
20        return fullName;
21    }
22 
23    public void setFullName(String fullName) {
24        this.fullName = fullName;
25    }
26 
27    public String getAge() {
28        return age;
29    }
30 
31    public void setAge(String age) {
32        this.age = age;
33    }
34 
35    public String getStatus() {
36        return status;
37    }
38 
39    public void setStatus(String status) {
40        this.status = status;
41    }
42 
43    public String getId() {
44        return id;
45    }
46 
47    public void setId(String id) {
48        this.id = id;
49    }
50 
51    public Address getAddress() {
52        return address;
53    }
54 
55    public void setAddress(Address address) {
56        this.address = address;
57    }
58 
59    public String toString() {
60        return this.id + "," this.fullName;
61    }
62}

Address.java

1package net.javabeat.springdata.jpa.data;
2 
3import java.io.Serializable;
4 
5public class Address implements Serializable{
6 
7    private static final long serialVersionUID = 1L;
8 
9    private Long addressId;
10 
11    private String addressValue;
12 
13    public Long getAddressId() {
14        return addressId;
15    }
16    public void setAddressId(Long addressId) {
17        this.addressId = addressId;
18    }
19    public String getAddressValue() {
20        return addressValue;
21    }
22    public void setAddressValue(String addressValue) {
23        this.addressValue = addressValue;
24    }
25}

5. Spring Data Redis Example Application

The below Java Class, is just an executable application that developed for persisting an User entity associated with an Address inside Redis key/value database. Note that the using of this concept entity is just theoretical uses and it doesn’t mean anything when it comes to apply it inside the Redis.

Executable.java

1package net.javabeat.springdata.executable;
2 
3import net.javabeat.springdata.beans.RegistrationBean;
4import net.javabeat.springdata.jpa.data.Address;
5import net.javabeat.springdata.jpa.data.User;
6 
7import org.springframework.context.support.ClassPathXmlApplicationContext;
8 
9public class Executable {
10    public static RegistrationBean registrationBean;
11    public static ClassPathXmlApplicationContext context;
12 
13    static {
14        // Acquire Context
15        context = new ClassPathXmlApplicationContext("SpringContext.xml");
16    }
17 
18    public static void main(String [] args) throws Exception{
19        // Create User
20        createUser();
21    }
22 
23    public static void createUser(){
24        User user = new User();
25        user.setId("20011202");
26        user.setFullName("Susa Richard");
27        user.setStatus("A");
28        user.setAge("30");
29        Address address = new Address();
30        address.setAddressValue("UK/Manchester");
31        user.setAddress(address);
32        RegistrationBean bean = (RegistrationBean)context.getBean("registrationBean");
33        // Persisting Inside the Hash User object
34        bean.getRedisTemplate().opsForHash().put("UserA", user.hashCode(),user);
35        // Retrieving the User object from the Redis by using the suggested key
36        User x = (User)bean.getRedisTemplate().opsForHash().get("UserA", user.hashCode());
37        System.out.println(x);
38    }
39}

6. Query Redis Persisted Store

The below snapshot shows you the User object that already persisted inside the Hash persistent store inside the Redis Key/Value database.Redis Persisted Data

- See more at: http://www.javabeat.net/spring-data-redis-example/#sthash.fFpl5LKF.dpuf



source - http://www.javabeat.net/spring-data-redis-example/















Posted by linuxism
,