简介
关系:StringRedisTemplate继承RedisTemplate。
数据不通用:StringRedisTemplate只能管理StringRedisTemplate的数据,RedisTemplate只能管理RedisTemplate的数据。
区别
RedisTemplate:使用JdkSerializationRedisSerializer,存时先将数据先序列化成字节数组再存入Redis。
StringRedisTemplate:使用StringRedisSerializer,存时先将数据先序列化成字符串再存入Redis。
使用场景
StringRedisTemplate:Redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型。
RedisTemplate:数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象。
RedisTemplate使用时常见问题
RedisTemplate 中存取数据都是字节数组。当Redis中存入的数据是可读形式而非字节数组时,使用RedisTemplate取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。
RedisTemplate
序列化
针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
项 | 简介 | 使用场景 | 优点 | 缺点 |
JdkSerializationRedisSerializer | JDK序列化(默认) | 用于对象的存取 | 简单 | 存储的为二进制数据,可读性差,占空间大。 |
StringRedisSerializer | “new String(bytes, charset)”和“string.getBytes(charset)”的直接封装 | 1.key或value为字符串 2.数据要被工具解析 | 最轻量和高效 | |
Jackson2JsonRedisSerializer | javabean与json之间的转换 | 将对象转成json存在redis中,也可将json转成对象 | ||
GenericJackson2JsonRedisSerializer | javabean与json之间的转换。与Jackson2JsonRedisSerializer的不同:会带包全路径,转为通用类型 | |||
OxmSerializer | javabean与xml之间的转换。 目前可用的三方:jaxb,apache-xmlbeans | 编程将会有些难度,而且效率最低 |
指定序列化的方法
package com.example.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisTemplateConfig {
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// 指定要序列化的域:field、get和set,以及修饰符范围,ANY是都有包括private和public
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 1.指定序列化输入的类型,序列化时将对象全类名一起保存下来,便于以后进行反序列化
// 2.类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //老写法
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
这样:自己注入RedisTemplate进行操作时,就会使用上边的配置了。
注意:如果配置spring-session使用json,上边这种是不可以的,需要这样写:
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
public class RedisCacheConfig {
// 将spring session中的序列化器替换成json
public RedisSerializer springSessionDefaultRedisSerializer() {
return RedisSerializer.json();
//等价于:return new GenericJackson2JsonRedisSerializer();
}
// 将spring cache中的序列化器替换成json
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
//等价于:return RedisCacheConfiguration.defaultCacheConfig()
// .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}
方法
其他网址
RedisTemplate常用集合使用说明(一) - - ITeye博客
【深入浅出SpringBoot】RedisTemplate使用方法归纳 - 简书
Key类型操作
接口 | 描述 | 获取方法 |
ValueOperations | 操作Redis String(或者Value)类型数据。 | redisTemplate.opsForValue(); |
ListOperations | 操作Redis List类型数据 | redisTemplate.opsForList(); |
SetOperations | 操作Redis Set类型数据 | redisTemplate.opsForSet(); |
ZSetOperations | 操作Redis ZSet(或者Sorted Set)类型数据 | redisTemplate.opsForZSet(); |
HashOperations | 操作Redis Hash类型数据 | redisTemplate.opsForHash(); |
HyperLogLogOperations | 操作Redis HyperLogLog类型数据,比如:pfadd,pfcount,... | |
GeoOperations | 操作Redis Geospatial类型数据,比如:GEOADD,GEORADIUS,…) |
Key绑定操作
可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key
接口 | 描述 |
BoundValueOperations | Redis字符串(或值)键绑定操作 |
BoundListOperations | Redis列表键绑定操作 |
BoundSetOperations | Redis Set键绑定操作 |
BoundZSetOperations | Redis ZSet(或Sorted Set)键绑定操作 |
BoundHashOperations | Redis Hash键绑定操作 |
BoundGeoOperations | Redis Geospatial 键绑定操作 |
基本操作
其他网址
以下所有操作均同时适用于StringRedisTemplate。
//注入对象(RedisTemplate自动关闭Redis连接。)
@Autowired
private RedisTemplate redisTemplate;
String
方法 | 作用 |
redisTemplate.opsForValue().set("test", "100"); | 存入数据,不设置过期时间,永久性保存 |
redisTemplate.setIfAbsent("test", "100"); | 判断key值是否存在,存在则不存储,不存在则存储 |
redisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS); | 存入数据和设置缓存时间 |
redisTemplate.opsForValue().append("test","Hello"); | 若key已存在且是一字符串,将该值追加到字符串的末尾。若键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET。 |
redisTemplate.opsForValue().get("test") | 根据key获取缓存中的val |
redisTemplate.opsForValue().size("test")); | 返回key所对应的value值的长度 |
redisTemplate.boundValueOps("test").increment(-1); | val-1 |
redisTemplate.boundValueOps("test").increment(1); | val +1 |
List
方法 | 作用 |
Long ret = redisTemplate.opsForList().leftPush("list","java"); | 左插。若键不存在,则先将其创建为空列表 |
String[] strs = new String[]{"1","2","3"}; Long ret = redisTemplate.opsForList().leftPushAll("list",strs); | 批量把一个数组插入到列表中 |
redisTemplate.opsForList().set("list",1,"setValue"); | 在列表中index的位置设置value |
String leftPop = redisTemplate.opsForList().leftPop("redisList"); | 从左往右遍历。(该值同时被从列表中删除) |
redisTemplate.opsForList().index("listRight",2) | 根据下标获取值(下标从0开始) |
List<String> range = redisTemplate.opsForList().range("redisList", 0, -1); | 查询全部元素 |
List<String> range1 = redisTemplate.opsForList().range("redisList", 0, 3); | 查询前三个元素 |
Long size = redisTemplate.opsForList().size("redisList"); | 查询list大小 |
Set
方法 | 作用 |
redisTemplate.opsForSet().add("red_123", "1","2","3"); | 向指定key中存放set集合 |
Set<String> resultSet = redisTemplate.opsForSet().members("red_123"); | 根据key获取set集合 |
redisTemplate.opsForSet().isMember("red_123", "1") | 根据key查看集合中是否存在指定数据 |
Hash
方法 | 作用 |
redisTemplate.opsForHash().put("hash", "hash1", "value1"); | 往key对应的map中新增(key1,value1) |
Map<String, String> map = new HashMap<String, String>(); map.put("map1", "fiala1"); map.put("map2", "fiala2"); redisTemplate.opsForHash().putAll("hash", map); | 将Java的HashMap存入redis |
Object o = redisTemplate.opsForHash().get("hash", "hash1"); | 获取key对应的map中hash1的值 |
Map<Object, Object> hash = redisTemplate.opsForHash().entries("hash"); | 获取hash对应的map。 |
Set<Object> hash1 = redisTemplate.opsForHash().keys("hash"); | 获取hash对应的map中全部子hash集合 |
List<Object> hash2 = redisTemplate.opsForHash().values("hash"); | 获取hash对应的map中全部value集合 |
Boolean aBoolean = redisTemplate.opsForHash().hasKey("hash", "hash1"); | 判断key对应的map中是否存在键 |
Cursor<Map.Entry<Object, Object>> curosr = redisTemplate.opsForHash().scan("hash", ScanOptions.ScanOptions.NONE); while(curosr.hasNext()){ | 使用Cursor在key的hash中迭代,相当于迭代器。 |
Long delete = redisTemplate.opsForHash().delete("hash", "key1", "key2"); | 删除key对应的map中多个子hash(可变参数) |
其他
方法 | 作用 |
redisTemplate.delete("test"); | 根据key删除缓存 |
redisTemplate.hasKey("546545"); | 检查key是否存在,返回boolean值 |
redisTemplate.expire("red_123",1000 , TimeUnit.MILLISECONDS); | 设置过期时间 |
redisTemplate.getExpire("test") | 根据key获取过期时间。不设的话为-1 |
redisTemplate.getExpire("test",TimeUnit.SECONDS) | 根据key获取过期时间并换算成指定单位 |
配置
其他网址
SpringBoot2.0+整合redis,使用 RedisTemplate操作redis - 知乎
说明
CacheManage的配置是为了配合注解使用redis而配置的,然而在我的开发使用中不太习惯使用注解,
首先注解确实可以更方便,但是复杂的操作和异常无法处理,这就使的灵活性有所下降,当然一些简单的处理就完全可以配合使用注解了。在下面的测试中,将使用RedisTemplate进行操作 。
配置类
package com.novacloud.information.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
//开启注解
public class RedisConfig {
//如使用注解的话需要配置cacheManager
CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
//设置默认超过期时间是1天
defaultCacheConfig.entryTtl(Duration.ofDays(1));
//初始化RedisCacheManager
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
return cacheManager;
}
// 以下两种redisTemplate自由根据场景选择
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}
}
操作实例
package com.neopte;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
(SpringRunner.class)
public class NeopteApplicationTests {
RedisTemplate redisTemplate;
public void contextLoads() {
//这里相当于redis对String类型的set操作
redisTemplate.opsForValue().set("test","helloworld");
//这里相当于redis对String类型的get操作
String test = (String)redisTemplate.opsForValue().get("test");
System.out.println(test);
}
}
StringRedisTemplate
基本操作
其他网址
如何使用StringRedisTemplate操作Redis详
实例
("/user")
public class UserResource {
private static final Logger log = LoggerFactory.getLogger(UserResource.class);
private UserService userService;
public StringRedisTemplate stringRedisTemplate;
("/num")
public String countNum() {
String userNum = stringRedisTemplate.opsForValue().get("userNum");
if(StringUtils.isNull(userNum)){
stringRedisTemplate.opsForValue().set("userNum", userService.countNum().toString());
}
return userNum;
}
}
进阶用法
StringRedisTemplate实现事务
stringRedisTemplate.setEnableTransactionSupport(true);
try {
stringRedisTemplate.multi();//开启事务
stringRedisTemplate.opsForValue().increment("count", 1);
stringRedisTemplate.opsForValue().increment("count1", 2);
//提交
stringRedisTemplate.exec();
}catch (Exception e){
log.error(e.getMessage(), e);
//开启回滚
stringRedisTemplate.discard();
}
StringRedisTemplate开启事务之后,不释放连接。如果我们使用Spring事务管理不存在这个问题
StringRedisTemplate实现乐观锁
redisTemplate.watch("key"); // 1
redisTemplate.multi();
redisTemplate.boundValueOps("key").set(""+id);
List<Object> list= redisTemplate.exec();
System.out.println(list);
if(list != null ){
//操作成功
System.out.println(id+"操作成功");
}else{
//操作失败
System.out.println(id+"操作失败");
}
StringRe
StringRedisTemplate实现分布式锁
String lockKey = "key";
String lockValue = lockKey+System.currentTimeMillis();
// value需要记住用于解锁
while (true){
Boolean ifPresent = stringRedisTemplate.opsForValue().
setIfAbsent("redis-lock:" + lockKey, lockValue, 3, TimeUnit.SECONDS);
if (ifPresent){
log.info("get redis-lock success");
break;
}
}
//解锁
String lockKey = "key";
String lockValue = lockKey + System.currentTimeMillis();
boolean result = false;
// value需要记住用于解锁
stringRedisTemplate.watch("redis-lock:" + lockKey);
String value = stringRedisTemplate.opsForValue().get("redis-lock:" + lockKey);
if (null == value){
result = true;
}else if (value.equals(lockValue)) {
stringRedisTemplate.delete("redis-lock:" + lockKey);
result = true;
}
stringRedisTemplate.unwatch();