当前位置: 代码迷 >> 综合 >> SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择
  详细解决方案

SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择

热度:63   发布时间:2023-09-18 12:43:40

前言

为了方便搭建项目,本文是直接在windows下安装redis。
安装步骤参考:Windows下载安装Redis、可视化工具安装

一、使用步骤

1.引入依赖

<!-- Redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.环境配置

SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择

3.Redis工具类

package com.local.dev.root.devroot.common.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Component
@SuppressWarnings("ALL")
public class RedisUtil {
    @Autowiredprivate StringRedisTemplate redisTemplate;/*** 指定缓存失效时间** @param key 键* @param time 时间(秒)*/public void expire(String key, long time) {
    try {
    if (time > 0) {
    redisTemplate.expire(key, time, TimeUnit.SECONDS);}} catch (Exception e) {
    e.printStackTrace();}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {
    return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {
    try {
    return redisTemplate.hasKey(key);} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/public void del(String... key) {
    if (key != null && key.length > 0) {
    if (key.length == 1) {
    redisTemplate.delete(key[0]);} else {
    redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}}//============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {
    return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key 键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {
    try {
    redisTemplate.opsForValue().set(key, String.valueOf(value));return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** @param key 键* @param value 值* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {
    try {
    if (time > 0) {
    redisTemplate.opsForValue().set(key, String.valueOf(value), time, TimeUnit.SECONDS);} else {
    set(key, value);}return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 递增** @param key 键* @return*/public long incr(String key, long delta) {
    if (delta < 0) {
    throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** @param key 键* @return*/public long decr(String key, long delta) {
    if (delta < 0) {
    throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}//================================Map================================= /*** HashGet** @param key 键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key, String item) {
    return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {
    return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {
    try {
    redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** HashSet 并设置时间** @param key 键* @param map 对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {
    try {
    redisTemplate.opsForHash().putAll(key, map);if (time > 0) {
    expire(key, time);}return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {
    try {
    redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {
    try {
    redisTemplate.opsForHash().put(key, item, value);if (time > 0) {
    expire(key, time);}return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key 键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {
    redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key 键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {
    return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key 键* @param item 项* @param by 要增加几(大于0)* @return*/public double hincr(String key, String item, double by) {
    return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key 键* @param item 项* @param by 要减少记(小于0)* @return*/public double hdecr(String key, String item, double by) {
    return redisTemplate.opsForHash().increment(key, item, -by);}//============================Set=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {
    try {
    return Collections.singleton(redisTemplate.opsForSet().members(key));} catch (Exception e) {
    e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key 键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {
    try {
    return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key 键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {
    try {
    return redisTemplate.opsForSet().add(key, String.valueOf(values));} catch (Exception e) {
    e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key 键* @param time 时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, String... values) {
    try {
    Long count = redisTemplate.opsForSet().add(key, values);if (time > 0) {
    expire(key, time);}return count;} catch (Exception e) {
    e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {
    try {
    return redisTemplate.opsForSet().size(key);} catch (Exception e) {
    e.printStackTrace();return 0;}}/*** 移除值为value的** @param key 键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {
    try {
    Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {
    e.printStackTrace();return 0;}}//===============================list================================= /*** 获取list缓存的内容** @param key 键* @param start 开始* @param end 结束 0 到 -1代表所有值* @return*/public List<String> lGet(String key, long start, long end) {
    try {
    return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {
    e.printStackTrace();return null;}}/*** 获取list缓存的长度** @param key 键* @return*/public long lGetListSize(String key) {
    try {
    return redisTemplate.opsForList().size(key);} catch (Exception e) {
    e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** @param key 键* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key, long index) {
    try {
    return redisTemplate.opsForList().index(key, index);} catch (Exception e) {
    e.printStackTrace();return null;}}/*** 将list放入缓存** @param key 键* @param value 值* @return*/public boolean lSet(String key, Object value) {
    try {
    redisTemplate.opsForList().rightPush(key, (String) value);return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {
    try {
    redisTemplate.opsForList().rightPush(key, (String) value);if (time > 0) {
    expire(key, time);}return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @return*/public boolean lSet(String key, List<Object> value) {
    try {
    redisTemplate.opsForList().rightPushAll(key, String.valueOf(value));return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {
    try {
    redisTemplate.opsForList().rightPushAll(key, String.valueOf(value));if (time > 0) {
    expire(key, time);}return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** @param key 键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {
    try {
    redisTemplate.opsForList().set(key, index, (String) value);return true;} catch (Exception e) {
    e.printStackTrace();return false;}}/*** 移除N个值为value** @param key 键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {
    try {
    Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {
    e.printStackTrace();return 0;}}
}

4.测试

测试代码:

@RunWith(SpringRunner.class)
@SpringBootTest(classes =DevRootApplication.class)
class DevRootApplicationTests {
    @Autowiredprivate RedisUtil redisUtil;@Testpublic void testRedis1() {
    String userId = "123456";redisUtil.set(userId,"我是userId:123456");String value = (String) redisUtil.get(userId);System.out.println(value);}
}

测试结果:
SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择

没有问题对不对,但存储value为复杂的对象类型时,再来看看测试结果

@Test
public void testRedis2() {
    String userId = "7890";SysUser user = new SysUser();user.setId("1234567890");user.setLoginName("loginName");user.setUserName("用户名");user.setLastLoginTime(new Date());user.setLogicDel(1);redisUtil.set(userId, user);SysUser userInfo = (SysUser) redisUtil.get(userId);System.out.println(userInfo);
}

SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择
出现错误。

5.StringRedisTemplate与RedisTemplate

会出现上述错误,是因为工具类中,我们使用的StringRedisTemplate。

StringRedisTemplate源码
SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择
可以看到StringRedisTemplate 是继承 RedisTemplate的<String,String>泛型,序列化使用的是StringRedisSerializer类。反序列化,也是一个得到String。
StringRedisTemplate适用于k-v都是String类型,如果k-v是Object类型,则需要自定义 RedisTemplate

自定义配置类

package com.local.dev.root.devroot.common.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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;@Configuration
public class RedisConfig {
    @Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    // String的序列化方式StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// Jackson2JsonRedisSerializer序列化类Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();template.setConnectionFactory(factory);// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}

修改RedisUtil代码

@Autowired
private RedisTemplate<String, Object> redisTemplate;

将代码中所有value转化为string的地方去掉String.valueOf(value)(String)value 改为value

 @Testpublic void testRedis2() {
    String userId = "7890-redis";SysUser user = new SysUser();user.setId("1234567890");user.setLoginName("loginName");user.setUserName("用户名");user.setLastLoginTime(new Date());user.setLogicDel(1);redisUtil.set(userId, user);SysUser userInfo = (SysUser) redisUtil.get(userId);System.out.println(userInfo);}

测试结果
SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择
SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择

总结

整合Redis,主要注意设置下key和value的序列化方式,StringRedisTemplate与RedisTemplate的对比和选择。

对比
1.StringRedisTemplate继承了RedisTemplate。
2.RedisTemplate是一个泛型类,而StringRedisTemplate则不是。
3.StringRedisTemplate只能对key=String,value=String的键值对进行操作,RedisTemplate可以对任何类型的key-value键值对操作。
4.序列化的方式不同,StringRedisTemplate使用的是StringRedisSerializer类;RedisTemplate使用的是JdkSerializationRedisSerializer类,但最终都是得到了一个字节数组。反序列化,则是一个得到String,一个得到Object。

选择
简单来说,存取数据为String类型是,用StringRedisTemplate即可。而数据为复杂的对象类型,使用RedisTemplate。

? 上一章:SpringBoot —— 统一异常处理
? 下一章:SpringBoot —— Filter过滤器的使用