引用依赖
compile("org.springframework.data:spring-data-redis:1.3.2.RELEASE")
compile("com.fasterxml.jackson.core:jackson-databind:2.4.1.3")
compile("redis.clients:jedis:2.5.2")
testCompile("junit:junit-dep:4.11")
Spring Data的另外一个关键特性,也就是面向模板的数据访问,在使用Redis的时候,为我们提供帮助。
Spring Data Redis提供了四个连接工厂供我们选择。
JedisConnectionFactory
JredisConnectionFactory
LettuceConnectionFactory
SrpConnectionFactory
将连接工厂设置为Spring的Bean
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisCF() {
JedisConnectionFactory c =new JedisConnectionFactory();
c.setHostName("0.0.0.0");
c.setPort(6379);
return c;
}
}
使用RedisTemplate
如果不使用模板, 也可以直接存储数据
RedisConnection conn = cf.getConnection();
conn.set("key".getBytes(),"someValue".getBytes());
byte[] bytesValue = conn.get("key".getBytes());
System.out.println(new String(bytesValue));
Spring Data Redis以模板的形式提供了较高等级的数据访问方案。实际上,Spring Data Redis提供了两个模板:
RedisTemplate, StringRedisTemplate
RedisTemplate可以极大地简化Redis数据访问,能够让我们持久化各种类型的key和value,并不局限于字节数组。在认识到key和value通常是String类型之后,StringRedisTemplate扩展了RedisTemplate,只关注String类型。
使用RedisTemplate的方法是
RedisTemplate<String, Product> redis = new RedisTemplate<String, Product>();
redis.setConnectionFactory(cf);
如果项目中经常使用RedisTemplate或StringRedisTemplate的话,你可以考虑将其配置为bean,然后注入到需要的地方。如下就是一个声明RedisTemplate的简单@Bean方法:
@Bean
public RedisTemplate<String, Product> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Product> redis = new RedisTemplate<String, Product>();
redis.setConnectionFactory(cf);
return redis;
}
RedisTemplate的很多功能是以子API的形式提供的,它们区分了单个值和集合值的场景:
简单DEMO, 因为例子比较简单, 就不一一解释
@Test
public void workingWithSimpleValues() {
//region Description
//直接使用 Connection
RedisConnection conn = cf.getConnection();
conn.set("key".getBytes(), "someValue".getBytes());
byte[] bytesValue = conn.get("key".getBytes());
System.out.println(new String(bytesValue));
//endregion
Product product = new Product();
product.setSku("9781617291203");
product.setName("Spring in Action");
product.setPrice(39.99f);
//简单使用
redis.opsForValue().set(product.getSku(), product);
Product found = redis.opsForValue().get(product.getSku());
assertEquals(product.getSku(), found.getSku());
assertEquals(product.getName(), found.getName());
assertEquals(product.getPrice(), found.getPrice(), 0.005);
}
@Test
public void workingWithLists() {
Product product = new Product();
product.setSku("9781617291203");
product.setName("Spring in Action");
product.setPrice(39.99f);
Product product2 = new Product();
product2.setSku("9781935182436");
product2.setName("Spring Integration in Action");
product2.setPrice(49.99f);
Product product3 = new Product();
product3.setSku("9781935182955");
product3.setName("Spring Batch in Action");
product3.setPrice(49.99f);
//使用List的值
redis.opsForList().rightPush("cart", product);
redis.opsForList().rightPush("cart", product2);
redis.opsForList().rightPush("cart", product3);
assertEquals(3, redis.opsForList().size("cart").longValue());
Product first = redis.opsForList().leftPop("cart");
Product last = redis.opsForList().rightPop("cart");
assertEquals(product.getSku(), first.getSku());
assertEquals(product.getName(), first.getName());
assertEquals(product.getPrice(), first.getPrice(), 0.005);
assertEquals(product3.getSku(), last.getSku());
assertEquals(product3.getName(), last.getName());
assertEquals(product3.getPrice(), last.getPrice(), 0.005);
assertEquals(1, redis.opsForList().size("cart").longValue());
}
@Test
public void workingWithLists_range() {
for (int i = 0; i < 30; i++) {
Product product = new Product();
product.setSku("SKU-" + i);
product.setName("PRODUCT " + i);
product.setPrice(i + 0.99f);
redis.opsForList().rightPush("cart", product);
}
assertEquals(30, redis.opsForList().size("cart").longValue());
List<Product> products = redis.opsForList().range("cart", 2, 12);
for (int i = 0; i < products.size(); i++) {
Product product = products.get(i);
assertEquals("SKU-" + (i + 2), product.getSku());
assertEquals("PRODUCT " + (i + 2), product.getName());
assertEquals(i + 2 + 0.99f, product.getPrice(), 0.005);
}
}
@Test
public void performingOperationsOnSets() {
Product product = new Product();
product.setSku("9781617291203");
product.setName("Spring in Action");
product.setPrice(39.99f);
redis.opsForSet().add("cart", product);
assertEquals(1, redis.opsForSet().size("cart").longValue());
}
@Test
public void performingOperationsOnSets_setOperations() {
for (int i = 0; i < 30; i++) {
Product product = new Product();
product.setSku("SKU-" + i);
product.setName("PRODUCT " + i);
product.setPrice(i + 0.99f);
redis.opsForSet().add("cart1", product);
if (i % 3 == 0) {
redis.opsForSet().add("cart2", product);
}
}
Set<Product> diff = redis.opsForSet().difference("cart1", "cart2");
Set<Product> union = redis.opsForSet().union("cart1", "cart2");
Set<Product> isect = redis.opsForSet().intersect("cart1", "cart2");
assertEquals(20, diff.size());
assertEquals(30, union.size());
assertEquals(10, isect.size());
Product random = redis.opsForSet().randomMember("cart1");
// not sure what to assert here...the result will be random
assertNotNull(random);
}
@Test
public void bindingToAKey() {
Product product = new Product();
product.setSku("9781617291203");
product.setName("Spring in Action");
product.setPrice(39.99f);
Product product2 = new Product();
product2.setSku("9781935182436");
product2.setName("Spring Integration in Action");
product2.setPrice(49.99f);
Product product3 = new Product();
product3.setSku("9781935182955");
product3.setName("Spring Batch in Action");
product3.setPrice(49.99f);
//注意,我们只在一个地方使用了条目的key,也就是调用boundListOps()的时候。对返回
// 的BoundListOperations执行的所有操作都会应用到这个key上。
BoundListOperations<String, Product> cart = redis.boundListOps("cart");
cart.rightPush(product);
cart.rightPush(product2);
cart.rightPush(product3);
assertEquals(3, cart.size().longValue());
Product first = cart.leftPop();
Product last = cart.rightPop();
assertEquals(product.getSku(), first.getSku());
assertEquals(product.getName(), first.getName());
assertEquals(product.getPrice(), first.getPrice(), 0.005);
assertEquals(product3.getSku(), last.getSku());
assertEquals(product3.getName(), last.getName());
assertEquals(product3.getPrice(), last.getPrice(), 0.005);
assertEquals(1, cart.size().longValue());
}
使用key和value的序列化器
当某个条目保存到Redis key-value存储的时候,key和value都会使用Redis的序列化器(serializer)进行序列化。Spring Data Redis提供了多个这样的序列化器,包括:
GenericToStringSerializer:使用Spring转换服务进行序列化;
JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
JdkSerializationRedisSerializer:使用Java序列化;
OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化;
StringRedisSerializer:序列化String类型的key和value。
这些序列化器都实现了RedisSerializer接口,如果其中没有符合需求的序列化器,那么你还可以自行创建。
例如,假设当使用RedisTemplate的时候,我们希望将Product类型的value序列化为JSON,而key是String类型。RedisTemplate的setKeySerializer()和setValueSerializer()方法就需要如下所示:
@Test
public void settingKeyAndValueSerializers() {
// need a local version so we can tweak the serializer
RedisTemplate<String, Product> redis = new RedisTemplate<String, Product>();
redis.setConnectionFactory(cf);
redis.setKeySerializer(new StringRedisSerializer());
redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class));
redis.afterPropertiesSet(); // if this were declared as a bean, you wouldn't have to do this
Product product = new Product();
product.setSku("9781617291203");
product.setName("Spring in Action");
product.setPrice(39.99f);
redis.opsForValue().set(product.getSku(), product);
Product found = redis.opsForValue().get(product.getSku());
assertEquals(product.getSku(), found.getSku());
assertEquals(product.getName(), found.getName());
assertEquals(product.getPrice(), found.getPrice(), 0.005);
StringRedisTemplate stringRedis = new StringRedisTemplate(cf);
String json = stringRedis.opsForValue().get(product.getSku());
assertEquals("{\"sku\":\"9781617291203\",\"name\":\"Spring in Action\",\"price\":39.99}", json);
}