spring把专门的数据操作独立封装在spring-data系列中,spring-data-redis自然是针对Redis的独立封装了。
1.5 功能测试类
上面的由于list、set在RedisConnection中没找到集合添加的方式(只看到可变参数的方式,这种实现麻烦,就不上了),有兴趣的童鞋可以自己研究下。
当前版本1.0.1,主要是将jedis、jredis、rjc以及srp等Redis Client进行了封装,同时支持事务。当然,当前版本不支持Sharding,据说Redis3.0会推出,期待吧。想必大家刚开始接触Spring的时候,都学习过Spring JDBC,如果是这样的话,接下来的代码,你们会觉得似曾相识。
一、熟悉的Spring JDBC模式(回调模式)
1.1 pom中添加spring、jedis依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>4.3.13.RELEASE</springframework.version>
<redis.version>2.9.0</redis.version>
<spring.redis.version>2.0.2.RELEASE</spring.redis.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-beanutils.version>1.9.0</commons-beanutils.version>
<commons-collections.version>3.2.1</commons-collections.version>
<commons-io.version>2.4</commons-io.version>
<commons-logging.version>1.1.1</commons-logging.version>
<commons-codec.version>1.10</commons-codec.version>
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.16</log4j.version>
<logback.version>1.1.2</logback.version>
</properties>
<repositories>
<!-- 有了仓库的组的概念, 我们只需要做一次引用就可以了 -->
<!-- Nexus中预设了2个仓库组,public repositories和public snapshot repositories -->
<repository>
<id>nexus</id>
<name>Team Nexus Repository</name>
<url>http://mvnrepository.com</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!--logger begin -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
<exclusion>
<groupId>oro</groupId>
<artifactId>oro</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- logger end -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
</dependencies>
1.2 新建application.properties资源文件
#\u6700\u5927\u5206\u914D\u7684\u5BF9\u8C61\u6570
redis.pool.maxActive=1024
#\u6700\u5927\u80FD\u591F\u4FDD\u6301idel\u72B6\u6001\u7684\u5BF9\u8C61\u6570
redis.pool.maxIdle=200
#\u5F53\u6C60\u5185\u6CA1\u6709\u8FD4\u56DE\u5BF9\u8C61\u65F6\uFF0C\u6700\u5927\u7B49\u5F85\u65F6\u95F4
redis.pool.maxWait=1000
#\u5F53\u8C03\u7528borrow Object\u65B9\u6CD5\u65F6\uFF0C\u662F\u5426\u8FDB\u884C\u6709\u6548\u6027\u68C0\u67E5
redis.pool.testOnBorrow=true
redis.pool.testWhileIdle=true
redis.pool.timeBetweenEvictionRunsMillis=60000
#IP
redis.ip=127.0.0.1
#Port
redis.port=6379
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd"
default-autowire="byName">
<!-- 以下为jedis单节点配置 -->
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:application.properties" />
<!-- jedis非切片化配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.pool.maxActive}" />
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<property name="maxWaitMillis" value="${redis.pool.maxWait}" />
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
<property name="testWhileIdle" value="${redis.pool.testWhileIdle}" />
<property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}" />
</bean>
<!-- jedis客户端连接工厂实现 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.ip}" />
<property name="port" value="${redis.port}" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
<!-- jedis 模版类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"></property>
<!-- 如果不配置Serializer,那么存储的时候智能使用String,
如果用User类型存储,那么会提示错误User can't cast to String!!! -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
</beans>
1.4实体类
public class User implements Serializable {
private static final long serialVersionUID = -1267719235225203410L;
private String uid;
private String address;
private String mobile;
private String postCode;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
@Override
public String toString() {
return "User [uid=" + uid + ", address=" + address + ", mobile="
+ mobile + ", postCode=" + postCode + "]";
}
}
1.5 功能测试类
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
*
* 案例参考:http://snowolf.iteye.com/blog/1667104
*
* 基本操作,存储String类型数据
*
* 目前redis并未实现集群技术,redis3.0之后版本可能出现集群技术,拭目以待
*
* spring-data-redis已经对jedis、jredis进行了封装,
* (1)完美兼容redis sentinel部署模式、
* --sentinel采用投票方式进行故障切换,而投票方式下原则是少数服从多数,所以sentinel一般至少是3台
* (2)兼容redis-cluster部署模式
* (3)单节点redis部署模式,
* 提供统一的API(RedisTemplate)来调用不同的部署模式,完全不用担心redis部署模式变化导致redis客户端代码做调整
*
* @Description:
* @version 1.0
* @Create: 2017年12月27日 下午12:35:13
* @Modification History
* @Date Author Version Description
*/
public class SpringJedisCallBackTest {
private static ApplicationContext context = null;
private static RedisTemplate<Serializable, Serializable> redisTemplate = null;
static {
context = new ClassPathXmlApplicationContext(
"classpath*:applicationContext.xml");
redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
}
public static void main(String[] args) {
User user = new User();
user.setUid("1111112255");
user.setAddress("上海市");
user.setMobile("");
user.setPostCode("");
/*
* save(user);
*
* user= read(user.getUid()); System.out.println("save==="+user);
*
* user.setAddress("hhhhhhhhhhhh");
*
* save(user);
*
* delete(user.getUid());
*
* user= read(user.getUid()); System.out.println("update==="+user);
*/
/*
* hashSave(user);
*
* user=hashRead(user.getUid()); System.out.println(user);
*/
/* listSave(user);
listRead(user);*/
/*setSave(user);
setRead(user);*/
zsetSave(user);
zsetRead(user);
}
/**
* 新增/修改 key不存在则新增,key存在则修改对应value值 基本String操作:set
*
* @param user
*/
public static void save(final User user) {
redisTemplate.execute(new RedisCallback<User>() {
public User doInRedis(RedisConnection connection)
throws DataAccessException {
connection.set(
redisTemplate.getStringSerializer().serialize(
"user.uid." + user.getUid()),
redisTemplate.getStringSerializer().serialize(
user.getAddress()));
return null;
}
});
}
/**
* 基本String操作:get
*
* @param uid
* @return
*/
public static User read(final String uid) {
return redisTemplate.execute(new RedisCallback<User>() {
public User doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.uid." + uid);
if (connection.exists(key)) {
byte[] value = connection.get(key);
String address = redisTemplate.getStringSerializer()
.deserialize(value);
User user = new User();
user.setAddress(address);
user.setUid(uid);
return user;
}
return null;
}
});
}
/**
* 基本String操作:del
*
* @param uid
*/
public static void delete(final String uid) {
redisTemplate.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.uid." + uid);
long count = connection.del(key);
System.out.println("count====" + count);
return null;
}
});
}
/**
* 保存hash表数据
*
* @param user
*/
public static void hashSave(final User user) {
redisTemplate.execute(new RedisCallback<User>() {
public User doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.info.uid." + user.getUid());
// 绑定hash key
BoundHashOperations<Serializable, byte[], byte[]> operations = redisTemplate
.boundHashOps(key);
operations.put(
redisTemplate.getStringSerializer()
.serialize("address"),
redisTemplate.getStringSerializer().serialize(
user.getAddress()));
operations
.put(redisTemplate.getStringSerializer().serialize(
"mobile"), redisTemplate.getStringSerializer()
.serialize(user.getMobile()));
operations.put(
redisTemplate.getStringSerializer().serialize(
"postCode"),
redisTemplate.getStringSerializer().serialize(
user.getPostCode()));
connection.hMSet(key, operations.entries());
return null;
}
});
}
/**
* hash表数据读取
*
* @param uid
* @return
*/
public static User hashRead(final String uid) {
return redisTemplate.execute(new RedisCallback<User>() {
public User doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.info.uid." + uid);
if (connection.exists(key)) {
List<byte[]> value = connection.hMGet(
key,
redisTemplate.getStringSerializer().serialize(
"address"),
redisTemplate.getStringSerializer().serialize(
"mobile"), redisTemplate
.getStringSerializer()
.serialize("postCode"));
User user = new User();
user.setAddress(redisTemplate.getStringSerializer()
.deserialize(value.get(0)));
user.setMobile(redisTemplate.getStringSerializer()
.deserialize(value.get(1)));
user.setPostCode(redisTemplate.getStringSerializer()
.deserialize(value.get(2)));
user.setUid(uid);
return user;
}
return null;
}
});
}
/**
* 保存list
* @param user
*/
public static void listSave(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.list." + user.getUid());
// 绑定hash key
BoundListOperations<Serializable, Serializable> operations = redisTemplate
.boundListOps(key);
operations.rightPush("11111");
operations.rightPush("22222");
operations.rightPush("33333");
operations.rightPush("44444");
}
/**
* 读取list
* @param user
*/
public static void listRead(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.list." + user.getUid());
// 绑定hash key
BoundListOperations<Serializable, Serializable> operations = redisTemplate
.boundListOps(key);
List<Serializable> list=operations.range(0, -1);
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Serializable serializable = (Serializable) iterator.next();
System.out.println(serializable);
}
}
/**
* 保存set
* @param user
*/
public static void setSave(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.set." + user.getUid());
// 绑定hash key
BoundSetOperations<Serializable, Serializable> operations = redisTemplate.boundSetOps(key);
operations.add("set11111");
operations.add("set33333");
operations.add("set22222");
}
/**
* 读取list
* @param user
*/
public static void setRead(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.set." + user.getUid());
// 绑定hash key
BoundSetOperations<Serializable, Serializable> operations = redisTemplate.boundSetOps(key);
Set<Serializable> set=operations.members();
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Serializable serializable = (Serializable) iterator.next();
System.out.println(serializable);
}
}
public static void zsetSave(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.zset." + user.getUid());
// 绑定hash key
BoundZSetOperations<Serializable, Serializable> operations = redisTemplate.boundZSetOps(key);
operations.add("zset1111", 7.0);
operations.add("zset2222",2.0);
operations.add("zset3333",3.0);
}
/**
* 读取list
* @param user
*/
public static void zsetRead(final User user) {
byte[] key = redisTemplate.getStringSerializer().serialize(
"user.zset." + user.getUid());
// 绑定hash key
BoundZSetOperations<Serializable, Serializable> operations = redisTemplate.boundZSetOps(key);
Set<TypedTuple<Serializable>> set=operations.rangeWithScores(0,-1);
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
TypedTuple serializable = (TypedTuple) iterator.next();
System.out.println(serializable.getValue()+","+serializable.getScore());
}
}
}
上面的由于list、set在RedisConnection中没找到集合添加的方式(只看到可变参数的方式,这种实现麻烦,就不上了),有兴趣的童鞋可以自己研究下。
二、Operations方式
上面回调的方式操作对于项目上来说实在是不易操作,费时费力(伙计们看到上面代码有没懒癌发作),接下来我们利用Spring替我们封装好的api简化下操作:
备注:本操作在一的基础上进行,配置文件不需要改动,只要改下测试类
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
*
* RedisTemplate高级操作 ValueOperations<K, V> valueOps; ListOperations<K, V>
* listOps; SetOperations<K, V> setOps; ZSetOperations<K, V> zSetOps;
* 案例参考1:https://www.cnblogs.com/edwinchen/p/3816938.html
* 案例参考2:http://www.cnblogs.com/luochengqiuse/p/4641256.html
*
* 目前redis并未实现集群技术,redis3.0之后版本可能出现集群技术,拭目以待
*
* spring-data-redis已经对jedis、jredis进行了封装,
* (1)完美兼容redis sentinel部署模式、
* --sentinel采用投票方式进行故障切换,而投票方式下原则是少数服从多数,所以sentinel一般至少是3台
* (2)兼容redis-cluster部署模式
* (3)单节点redis部署模式
* 提供统一的API(RedisTemplate)来调用不同的部署模式,完全不用担心redis部署模式变化导致redis客户端代码做调整
*
* @Description:
* @Create: 2017年12月27日 下午3:38:20
* @Modification History
* @Date Author Version Description
*/
public class SpringJedisRedisOperationsTest {
private static ApplicationContext context = null;
private static RedisTemplate<String, User> redisTemplate = null;
static {
context = new ClassPathXmlApplicationContext(
"classpath*:applicationContext.xml");
redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
}
public static void main(String[] args) {
User user = new User();
user.setUid("1111112333");
user.setAddress("上海市虹漕路A座4楼");
user.setMobile("mobile");
user.setPostCode("postcode");
/*
* save(user);
*
* user=read(user); System.out.println("user==="+user);
*/
/*
* listSave(user);
*
* listRead(user);
*/
/*
* hashSave(user); hashRead(user);
*/
/*setSave(user);
setRead(user);*/
zsetSave(user);
zsetRead(user);
}
public static void save(User user) {
redisTemplate.opsForValue().set(user.getUid(), user);
}
public static User read(User user) {
return redisTemplate.opsForValue().get(user.getUid());
}
public static void listSave(User user) {
List<User> users = new ArrayList<User>();
users.add(user);
redisTemplate.opsForList().leftPushAll(
"user.list.ops." + user.getUid(), users);
}
public static void listRead(User user) {
List<User> users = redisTemplate.opsForList().range(
"user.list.ops." + user.getUid(), 0, -1);
for (Iterator iterator = users.iterator(); iterator.hasNext();) {
User user1 = (User) iterator.next();
System.out.println(user1);
}
}
public static void hashSave(User user) {
Map<String, String> map = new HashMap<String, String>();
map.put("address", user.getAddress());
map.put("mobile", user.getMobile());
map.put("postCode", user.getPostCode());
redisTemplate.opsForHash()
.putAll("user.hash.ops." + user.getUid(), map);
}
public static void hashRead(User user) {
Map<Object, Object> map = redisTemplate.opsForHash().entries(
"user.hash.ops." + user.getUid());
Iterator<Entry<Object, Object>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<Object, Object> type = it.next();
System.out.println("key:" + type.getKey() + ",value:"
+ type.getValue());
}
}
public static void setSave(User user) {
/*
* 竟然没有批量新增方法????
*/
redisTemplate.opsForSet().add("user.set.ops." + user.getUid(), user);
}
public static void setRead(User user) {
Set<User> sets = redisTemplate.opsForSet().members(
"user.set.ops." + user.getUid());
for (Iterator iterator = sets.iterator(); iterator.hasNext();) {
User user1 = (User) iterator.next();
System.out.println("user1:" + user1);
}
}
public static void zsetSave(User user) {
Set<TypedTuple<User>> zsets=new HashSet<TypedTuple<User>>();
TypedTuple<User> tuple0=new DefaultTypedTuple(user, 1d);
zsets.add(tuple0);
redisTemplate.opsForZSet().add("user.zset.ops." + user.getUid(), zsets);
}
public static void zsetRead(User user) {
Set<TypedTuple<User>> zsets=new HashSet<TypedTuple<User>>();
TypedTuple<User> tuple0=new DefaultTypedTuple(user, 1d);
zsets.add(tuple0);
Set<User> users=redisTemplate.opsForZSet().range("user.zset.ops." + user.getUid(), 0, -1);
for (Iterator iterator = users.iterator(); iterator.hasNext();) {
User user2 = (User) iterator.next();
System.out.println("user2:"+user2);
}
}
}
上面的代码在操作单例server时,是不是简单许多,不过奇怪的是,spring竟然没有提供set的集合新增方式,之后可变参数方式,心累。到此为止,单例方式我觉得使用上述Operations应该足够在项目中使用了,下一篇准备研究下redis集群技术了,虽然redis自身没有提供集群解决方案,但是咱IT圈牛人还是很多的,redis集群解决方案还是存在的。