spring data redis repostory
转载自 https://blog.csdn.net/wlwlwlwl015/article/details/52863821
前言
Spring Data Redis是Spring Data大家族的一部分,提供了基于spring应用的简易配置与redis服务访问,它为存储与交互提供了低级(low-level)和高级的(high-level)封装与抽象,使得用户不必再关注底层,正如其官网给出的定义:
Spring Data Redis, part of the larger Spring Data family, provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns.
本篇blog主要记录一下Spring Data Redis在基于spring的web项目中的应用与配置,版本为当前最新的GA(1.7.4),同时也重点记录了该版本的新特性之一:Redis Repositories。
快速入门
按照官方的Quick Start我们先快速进行一个简单的入门,首先第一步是引入spring-data-redis的maven依赖,当前最新的GA版本是1.7.4:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.4.RELEASE</version>
</dependency>
同时需要注意官方文档给出了Requirements(必备环境):
如上图,Spring Data Redis 1.x需要JDK 6.0及以上版本,Spring需要4.2.8.RELEASE及以上版本,同时Redis也要保证在2.6.x或更高的版本。Spring Data Redis还依赖了commons-logging,commons-pool2以及jedis,所以接下来还需要引入jedis的依赖,此处我选择的版本是2.8.0(当前最新版为2.9.0 Jul, 2016):
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
依赖添加完毕后就可以开始配置编码了,依照官方的Quick Start,我们接下来应该做的是配置一个RedisTemplate:
如上图,我们这里稍做修改,添加我们自己的host-name、port、password等(如果有的话),还有别忘了在spring配置文件中添加schema(xmlns:p):
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注解扫描 -->
<context:component-scan base-package="com.wl.controller" />
<!-- jedisConnectionFactory -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="192.168.111.11" p:port="6379" />
<!-- redisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory" />
</beans>
如上所示,定义好了redisTemplate之后就可以在程序中注入测试了,看一个简单的Test Case:
package com.wl.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
@WebAppConfiguration
public class SpringRedisTest {
@Autowired
private RedisTemplate<String, String> template;
@Test
public void testFirst() {
// set username wlwlwlwl015
template.opsForValue().set("username", "wlwlwlwl015");
// get username
System.out.println(template.opsForValue().get("username"));
}
}
如上所示,16行通过@Autowired注入了RedisTemplate,22行和24行则是调用了redis最简单的两个字符串操作命令set key value
和get key
,运行junit test case可以成功看到键值的存取:
通过RedisTemplate存取完全没有问题,那么再看一下redis控制台下是否已成功添加了这对key-value:
如上图,仿佛有些不对劲,在key-value前面均加了一串字符串,这是由于RedisSerializer默认使用的是JdkSerializationRedisSerializer,这个具体后面再说,本小节仅仅是Quick Start,那么至此Quick Start就算成功完成了,接下来具体记录一下Spring Data Redis的常用API与项目的集成封装。
常用API与Serializer(序列化)
接下来看一下Spring Data Redis中的常用API与对象操作,毕竟这些才是我们在实际项目中会频繁用到的,依旧参考官方文档,可以看到一个Operational views的表格:
如上图,Spring Redis Data针对jedis客户端的api进行了重新归类与封装,将同一类型的操作封装为Operation接口,如上面的Key Type Operations和Key Bound Operations,其中Key Type Operations顾名思义也就是根据键类型给所有操作进行分类,类别如下:
- ValueOperations:简单K-V操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:map类型的数据操作
- ListOperations:list类型的数据操作
正如在Quick Start中用到的template.opsForValue()
也就是第一个ValueOperations了,而后面的Key Bound Operations则提供了对key的”bound”(绑定)便捷化操作的API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,举个简单例子:
@Test
public void testBound(){
BoundValueOperations<String, String> boundValueOps = template.boundValueOps("username");
System.out.println(boundValueOps.get());
boundValueOps.set("wlwlwlwl is good!");
System.out.println(boundValueOps.get());
}
很好理解,接下来介绍重点内容——序列化/反序列化(RedisSerializer),Spring Data Redis提供了多种可选择策略。官方文档的5.7小节针对Serializers只有一小段概述,下面是我截取的一小段重点内容:
Multiple implementations are available out of the box, two of which have been already mentioned before in this documentation: the StringRedisSerializer and the JdkSerializationRedisSerializer. However one can use OxmSerializer for Object/XML mapping through Spring 3 OXM support or either JacksonJsonRedisSerializer, Jackson2JsonRedisSerializer or GenericJackson2JsonRedisSerializer for storing data in JSON format.
如上所示,提供了多种开箱即用的实现,官方文档中已经被提过两种,分别是:
- StringRedisSerializer
- JdkSerializationRedisSerializer
第二种在上面也提过了,它是RedisTemplate提供的默认序列化方式,在源码中可以看到:
private RedisSerializer<?> defaultSerializer;
private RedisSerializer keySerializer = null;
private RedisSerializer valueSerializer = null;
private RedisSerializer hashKeySerializer = null;
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
/**
* Constructs a new <code>RedisTemplate</code> instance.
*/
public RedisTemplate() {}
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}
如上所示,23行指定默认defaultSerializer为JdkSerializationRedisSerializer,之所以上面通过RedisTemplate设置的key-value在redis控制台看加了一串字符串,是因为JdkSerializationRedisSerializer对key和value都进行了序列化,变成了字节序列(byte[]),然后再调用jedis进行存储的,而StringRedisSerializer是根据指定的charset对数据的字节序列编码成string,更适用于字符串场景,相当于new String(bytes, charset)
和string.getBytes(charset)
的直接封装,也更加轻量与高效,所以此处将默认的JdkSerializationRedisSerializer替换成StringRedisSerializer就可以正常存取键值了,在我们的RedisTemplate中加入如下配置:
<!-- redisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory">
<!-- 序列化方式 建议key/hashKey采用StringRedisSerializer。 -->
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
再回头看一下官方文档中关于Serializer的那一小段介绍,还提到了OxmSerializer和JacksonJsonRedisSerializer这两种策略,顾名思义,前者提供了将javabean与xml之间的转换能力(xml格式存储),而后者提供了javabean与json之间的转换能力(json格式存储,且依赖jackson-json工具包),由于实际项目中最常用的还是针对序列化对象的存取,所以接下来就记录一下对象操作,但并考虑使用以上两种,因为无论是json还是xml,他们本身仍然是String,并且以上两种策略封装和编程都较为复杂,性能也存在一些问题,在当前版本中我们有更好的选择,Spring Data Redis在最新版1.7.x中为我们提供了Redis Repositories,可以无缝的转换并存储domain objects,使用的数据类型为哈希(hash),下面重点看一下Redis Repositories的应用。
Redis Repositories
在Spring Data Redis1.7的新特性中,除了支持Redis集群外,我们还可以看到另一条重要的新特性——Spring Data Repository abstractions :
首先看一下官方文档中对Redis Repositories的简介:
如上所示,正如官方文档的介绍,Redis Repositories允许通过redis的hash类型无缝的存储以及转换领域对象,应用了自定义的映射策略并且利用了二级索引。接下来具体看一下用法,还需要注意一下如果要使用Redis Repositories,那么redis服务器的版本不能低于2.8:
Redis Repositories requires at least Redis Server version 2.8.0.
接下来看一下如何使用。
基础用法(Usage)
根据官方文档,首先第一步是在我们的实体bean上添加注解@RedisHash
,并且在标识属性(或主键)上添加@Id
注解,这两个注解负责创建用于存储对象hash的key,例如:
package com.wl.bean;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
@RedisHash("users")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
@Id
private String username;
private String password;
private String nickname;
private String email;
private String filePath;
public User() {}
}
第二步需要创建一个repository接口来存取数据,根据官方的Example改写后如下:
package com.wl.dao;
import org.springframework.data.repository.CrudRepository;
public interface BaseRepository<T> extends CrudRepository<T, String> {
}
用过spring data的话应该对这个接口比较熟悉了,CrudRepository为我们提供了一组基础的CRUD方法,有了这个基础接口之后,我们下面需要做的就通过Spring配置将bean和这个接口关联起来,正如官方文档的原话:
As our repository extends CrudRepository it provides basic CRUD and finder operations. The thing we need in between to glue things together is the according Spring configuration.
以User为例,我们再创建一个UserRepository接口:
package com.wl.dao;
import com.wl.bean.User;
public interface UserRepository extends BaseRepository<User>{
}
下面看一下这个配置类:
package com.wl.dao;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.stereotype.Component;
import com.wl.bean.User;
@Component("userDao")
@EnableRedisRepositories
public class UserDao {}
如上所示,关键点就是@EnableRedisRepositories
,该注解表示启用Repositories,接下来我们就可以将UserRepository注入到我们的业务组件中使用了,下面是一个简单的测试方法:
package com.wl.test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.wl.bean.User;
import com.wl.dao.UserRepository;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
@WebAppConfiguration
public class SpringRedisTest {
@Autowired
private UserRepository repo;
@Test
public void testRepositorySave(){
//User u = new User("wangliang", "123", "raito", "aaa@qq.com");
//repo.save(u);
List<User> users = new ArrayList<User>();
users.add(new User("shanshan", "123", "杉杉", "aaa@qq.com"));
users.add(new User("xiaoming", "123", "小明", "aaa@qq.com"));
users.add(new User("xiaohong", "123", "小红", "aaa@qq.com"));
repo.save(users);
}
@Test
public void testRepositoryGet(){
//User u = repo.findOne("wangliang");
//System.out.println(u);
Iterator<User> iterator = repo.findAll().iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
如上所示,主要测试了List的存取,首先运行第一个存List的测试方法:
可以看到测试通过,接下来在redis控制台看一下数据:
如上图,可以看到所有hash的键格式均是@RedisHash("users")
的参数值和@Id
注解的属性名拼接而成的(keyspace:id),所以再次强调@Id
务必要标记主键或者是数据库中的唯一标识符。save正常,再来看一下常用的findAll,运行第二个测试方法:
如上所示,测试通过,并且List中的数据可以成功返回并输出,Redis Repository的其它api就不再一一演示,可以发现用起来确实很爽,很强大,关于基础的使用方法(Usage)就暂且介绍到这里。
对象哈希映射(Object to Hash Mapping)
回顾上一小节,Redis Repository支持通过hash类型持久化对象,将对象的每个属性都映射的很好:
如上图,_class
属性还映射了全类名,那么Redis Repository是如何正确映射对象的全部属性呢?根据官方文档的说明,实际上是通过一个RedisConverter(转换器)来实现的,这个转换器的默认实现就是org.springframework.core.convert.converter.Converter
,在上图的映射列表中,所有的映射属性都属于Simple Type,官方文档中给出了一张默认的映射规则表:
如上图,分为5种类型,分别是:
- 简单类型(Simple Type)
- 符合类型(Complex Type)
- 简单列表类型(List of Simple Type)
- 简单K-V映射类型(Map of Simple Type)
- 符合列表类型(List of Complex Type)
- 符合K-V映射类型(Map of Complex Type)
每种映射类型都提供了对应的例子,当然这都是官方默认提供的,我们还可以通过程序自定义,由于我目前的项目只涉及简单类型,所以关于自定义映射就不做过多说明,有需求的朋友可以参考官方文档。
Keyspaces
keyspace用于创建hash键的前缀,默认的前缀是getClass().getName()
即全类名,这里的默认意思就是说当我们的@RedisHash注解没有指定参数时,默认会用全类名做为前缀:
如上图,这个hash键的前缀就是默认生成的,除了通过修改@RedisHash注解,还可以使用编程的形式来指定前缀,此时就需要创建一个自定义的KeyspaceConfiguration:
package com.wl.util;
import java.util.Collections;
import org.springframework.data.redis.core.convert.KeyspaceConfiguration;
import com.wl.bean.User;
public class MyKeyspaceConfiguration extends KeyspaceConfiguration {
@Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(User.class, "userme"));
}
}
如上所示,很好理解,这段代码的作用就是将keyspace指定为字符串“userme”,定义好KeyspaceConfiguration之后在@EnableRedisRepositories注解中声明引用即可:
package com.wl.dao;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.stereotype.Component;
import com.wl.bean.User;
import com.wl.util.MyKeyspaceConfiguration;
@Component("userDao")
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class UserDao {
@Autowired
private UserRepository repo;
}
再次运行junit测试插入1条数据,可以看到此时的key前缀已经改变:
Redis Repository还有更多高级内容,如二级索引(Secondary Indexes)、对象过期时间(Time To Live)、查询(Queries and Query Methods)等等,感兴趣的朋友可以参考Spring Data Redis官方文档:http://docs.spring.io/spring-data/redis/docs/1.7.4.RELEASE/reference/html/
总结
简单介绍一下Spring Data Redis的基础用法以及1.7新特性——Redis Repositories,希望对遇到同样问题的朋友有所帮助,The End。
上一篇: 第2记:流程控制语句篇
下一篇: 网红时代 如何做好网红营销 你想好了吗?
推荐阅读
-
详解Spring Data JPA使用@Query注解(Using @Query)
-
Spring Data JPA使用Sort进行排序(Using Sort)
-
详解Spring Data JPA系列之投影(Projection)的用法
-
spring data jpa使用详解(推荐)
-
javaweb各种框架组合案例(六):springboot+spring data jpa(hibernate)+restful
-
Spring-Data-JPA整合MySQL和配置的方法
-
Spring Data JPA例子代码[基于Spring Boot、Mysql]
-
Spring Data JPA学习
-
浅谈Spring Boot中Redis缓存还能这么用
-
Spring boot如何快速的配置多个Redis数据源