Springboot使用RedisTemplate优雅地操作redis

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Springboot使用RedisTemplate优雅地操作redis

点击上方“Java知音”,选择“置顶公众号”

技术文章第一时间送达!

作者:Janti cnblogs.com/superfj/p/9232482.html

技术经验交流:  ,本篇示例代码放文末。

概述

本文内容主要

  • 关于spring-redis
  • 关于redis的key设计
  • redis的基本数据结构
  • 介绍redis与springboot的整合
  • sringboot中的redistemplate的使用
  • 关于redis的key设计

    介绍redis与springboot的整合

    关于spring-redis

    spring-data-redis针对jedis提供了如下功能:

    1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类

    2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口

  • ValueOperations:简单K-V操作
  • SetOperations:set类型数据操作
  • ZSetOperations:zset类型数据操作
  • HashOperations:针对map类型的数据操作
  • ListOperations:针对list类型的数据操作
  • SetOperations:set类型数据操作

    HashOperations:针对map类型的数据操作

    3.提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:

  • BoundValueOperations
  • BoundSetOperations
  • BoundListOperations
  • BoundSetOperations
  • BoundHashOperations
  • BoundSetOperations

    BoundSetOperations

    4.将事务操作封装,有容器控制。

    5.针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

    JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。

    StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。

    JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。【需要jackson-mapper-asl工具支持】

    OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】

    如果你的数据需要被第三方工具解析,那么数据应该使用StringRedisSerializer而不是JdkSerializationRedisSerializer。

    关于key的设计

    key的存活时间:

    无论什么时候,只要有可能就利用key超时的优势。一个很好的例子就是储存一些诸如临时认证key之类的东西。当你去查找一个授权key时——以OAUTH为例——通常会得到一个超时时间。

    这样在设置key的时候,设成同样的超时时间,Redis就会自动为你清除。

    关系型数据库的redis

    1: 把表名转换为key前缀 如, tag:
    2: 第2段放置用于区分区key的字段–对应mysql中的主键的列名,如userid
    3: 第3段放置主键值,如2,3,4…., a , b ,c
    4: 第4段,写要存储的列名
    例:user:userid:9:username

    Redis的数据类型

    String字符串

  • string是redis最基本的类型,一个key对应一个value。
  • string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
  • string类型是Redis最基本的数据类型,一个键最大能存储512MB。
  • string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

    String类型的操作参考:

    http://www.runoob.com/redis/redis-strings.html

    链表

  • redis列表是简单的字符串列表,排序为插入的顺序。列表的最大长度为2^32-1。
  • redis的列表是使用链表实现的,这意味着,即使列表中有上百万个元素,增加一个元素到列表的头部或尾部的操作都是在常量的时间完成。
  • 可以用列表获取最新的内容(像帖子,微博等),用ltrim很容易就会获取最新的内容,并移除旧的内容。
  • 用列表可以实现生产者消费者模式,生产者调用lpush添加项到列表中,消费者调用rpop从列表中提取,如果没有元素,则轮询去获取,或者使用brpop等待生产者添加项到列表中。
  • redis的列表是使用链表实现的,这意味着,即使列表中有上百万个元素,增加一个元素到列表的头部或尾部的操作都是在常量的时间完成。

    用列表可以实现生产者消费者模式,生产者调用lpush添加项到列表中,消费者调用rpop从列表中提取,如果没有元素,则轮询去获取,或者使用brpop等待生产者添加项到列表中。

    List类型的操作参考:

    http://www.runoob.com/redis/redis-lists.html

    集合

  • redis集合是无序的字符串集合,集合中的值是唯一的,无序的。可以对集合执行很多操作,例如,测试元素是否存在,对多个集合执行交集、并集和差集等等。
  • 我们通常可以用集合存储一些无关顺序的,表达对象间关系的数据,例如用户的角色,可以用sismember很容易就判断用户是否拥有某个角色。
  • 在一些用到随机值的场合是非常适合的,可以用 srandmember/spop 获取/弹出一个随机元素。同时,使用@EnableCaching开启声明式缓存支持,这样就可以使用基于注解的缓存技术。注解缓存是一个对缓存使用的抽象,通过在代码中添加下面的一些注解,达到缓存的效果。
  • 我们通常可以用集合存储一些无关顺序的,表达对象间关系的数据,例如用户的角色,可以用sismember很容易就判断用户是否拥有某个角色。

    Set类型的操作参考:

    http://www.runoob.com/redis/redis-sets.html

    ZSet 有序集合

  • 有序集合由唯一的,不重复的字符串元素组成。有序集合中的每个元素都关联了一个浮点值,称为分数。可以把有序看成hash和集合的混合体,分数即为hash的key。
  • 有序集合中的元素是按序存储的,不是请求时才排序的。
  • 有序集合中的元素是按序存储的,不是请求时才排序的。

    ZSet类型的操作类型

    http://www.runoob.com/redis/redis-sorted-sets.html

    Hash-哈希

  • redis的哈希值是字符串字段和字符串之间的映射,是表示对象的完美数据类型。
  • 哈希中的字段数量没有限制,所以可以在你的应用程序以不同的方式来使用哈希。
  • 哈希中的字段数量没有限制,所以可以在你的应用程序以不同的方式来使用哈希。

    Hash类型的操作参考:

    http://www.runoob.com/redis/redis-hashes.html

    springboot 与redis的整合

    pom文件

    依赖如下:

    
       parent
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-parent/artifactId
            version1.5.1.RELEASE/version
            relativePath/
        /parent
    
        dependencies
            !-- spring boot 配置 --
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-web/artifactId
            /dependency
    
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-thymeleaf/artifactId
            /dependency
    
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-test/artifactId
                scopetest/scope
            /dependency
    
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-data-redis/artifactId
            /dependency
    
    
        /dependencies
    

    application.properties

    
    # Redis数据库索引(默认为0)
    spring.redis.database=0  
    # Redis服务器地址
    spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379  
    # Redis服务器连接密码(默认为空)
    spring.redis.password=
    # 连接池最大连接数(使用负值表示没有限制)
    spring.redis.pool.max-active=8  
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.pool.max-wait=-1  
    # 连接池中的最大空闲连接
    spring.redis.pool.max-idle=8  
    # 连接池中的最小空闲连接
    spring.redis.pool.min-idle=0  
    # 连接超时时间(毫秒)
    spring.redis.timeout=0  
    

    redisTemplate的配置

    新建一个redisConfig类,进行相关bean的配置:

    
    package com.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.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.*;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     * @author janti
     * reids 相关bean的配置
     */
    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport {
    
        /**
         * 选择redis作为默认缓存工具
         * @param redisTemplate
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            return rcm;
        }
    
        /**
         * retemplate相关配置
         * @param factory
         * @return
         */
        @Bean
        public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) {
    
            RedisTemplateString, Object template = new RedisTemplate();
            // 配置连接工厂
            template.setConnectionFactory(factory);
    
            //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
            Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
    
            ObjectMapper om = new ObjectMapper();
            // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jacksonSeial.setObjectMapper(om);
    
            // 值采用json序列化
            template.setValueSerializer(jacksonSeial);
            //使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
    
            // 设置hash key 和value序列化模式
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(jacksonSeial);
            template.afterPropertiesSet();
    
            return template;
        }
    
        /**
         * 对hash类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public HashOperationsString, String, Object hashOperations(RedisTemplateString, Object redisTemplate) {
            return redisTemplate.opsForHash();
        }
    
        /**
         * 对redis字符串类型数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ValueOperationsString, Object valueOperations(RedisTemplateString, Object redisTemplate) {
            return redisTemplate.opsForValue();
        }
    
        /**
         * 对链表类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ListOperationsString, Object listOperations(RedisTemplateString, Object redisTemplate) {
            return redisTemplate.opsForList();
        }
    
        /**
         * 对无序集合类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public SetOperationsString, Object setOperations(RedisTemplateString, Object redisTemplate) {
            return redisTemplate.opsForSet();
        }
    
        /**
         * 对有序集合类型的数据操作
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public ZSetOperationsString, Object zSetOperations(RedisTemplateString, Object redisTemplate) {
            return redisTemplate.opsForZSet();
        }
    }
    

    spring-redis中使用了RedisTemplate来进行redis的操作,通过泛型的K和V设置键值对的对象类型。这里使用了string作为key的对象类型,值为Object。

    对于Object,spring-redis默认使用了jdk自带的序列化,不推荐使用默认了。所以使用了json的序列化方式

    对spring-redis对redis的五种数据类型也有支持

    HashOperations:对hash类型的数据操作 ValueOperations:对redis字符串类型数据操作 ListOperations:对链表类型的数据操作 SetOperations:对无序集合类型的数据操作 ZSetOperations:对有序集合类型的数据操作

    redis操作的工具类

    
    package com.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.util.Collection;
    import java.util.Date;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    @Component
    public class RedisService {
        @Autowired
        private RedisTemplateString, String redisTemplate;
    
        /**
         * 默认过期时长,单位:秒
         */
        public static final long DEFAULT_EXPIRE = 60 * 60 * 24;
    
        /**
         * 不设置过期时长
         */
        public static final long NOT_EXPIRE = -1;
    
    
    
    
        public boolean existsKey(String key) {
            return redisTemplate.hasKey(key);
        }
    
        /**
         * 重名名key,如果newKey已经存在,则newKey的原值被覆盖
         *
         * @param oldKey
         * @param newKey
         */
        public void renameKey(String oldKey, String newKey) {
            redisTemplate.rename(oldKey, newKey);
        }
    
        /**
         * newKey不存在时才重命名
         *
         * @param oldKey
         * @param newKey
         * @return 修改成功返回true
         */
        public boolean renameKeyNotExist(String oldKey, String newKey) {
            return redisTemplate.renameIfAbsent(oldKey, newKey);
        }
    
        /**
         * 删除key
         *
         * @param key
         */
        public void deleteKey(String key) {
            redisTemplate.delete(key);
        }
    
        /**
         * 删除多个key
         *
         * @param keys
         */
        public void deleteKey(String... keys) {
            SetString kSet = Stream.of(keys).map(k - k).collect(Collectors.toSet());
            redisTemplate.delete(kSet);
        }
    
        /**
         * 删除Key的集合
         *
         * @param keys
         */
        public void deleteKey(CollectionString keys) {
            SetString kSet = keys.stream().map(k - k).collect(Collectors.toSet());
            redisTemplate.delete(kSet);
        }
    
        /**
         * 设置key的生命周期
         *
         * @param key
         * @param time
         * @param timeUnit
         */
        public void expireKey(String key, long time, TimeUnit timeUnit) {
            redisTemplate.expire(key, time, timeUnit);
        }
    
        /**
         * 指定key在指定的日期过期
         *
         * @param key
         * @param date
         */
        public void expireKeyAt(String key, Date date) {
            redisTemplate.expireAt(key, date);
        }
    
        /**
         * 查询key的生命周期
         *
         * @param key
         * @param timeUnit
         * @return
         */
        public long getKeyExpire(String key, TimeUnit timeUnit) {
            return redisTemplate.getExpire(key, timeUnit);
        }
    
        /**
         * 将key设置为永久有效
         *
         * @param key
         */
        public void persistKey(String key) {
            redisTemplate.persist(key);
        }
    
    
    }
    

    redis的key工具类

    
    package com.util;
    
    /**
     * redisKey设计
     */
    public class RedisKeyUtil {
    
        /**
         * redis的key
         * 形式为:
         * 表名:主键名:主键值:列名
         *
         * @param tableName 表名
         * @param majorKey 主键名
         * @param majorKeyValue 主键值
         * @param column 列名
         * @return
         */
        public static String getKeyWithColumn(String tableName,String majorKey,String majorKeyValue,String column){
            StringBuffer buffer = new StringBuffer();
            buffer.append(tableName).append(":");
            buffer.append(majorKey).append(":");
            buffer.append(majorKeyValue).append(":");
            buffer.append(column);
            return buffer.toString();
        }
        /**
         * redis的key
         * 形式为:
         * 表名:主键名:主键值
         *
         * @param tableName 表名
         * @param majorKey 主键名
         * @param majorKeyValue 主键值
         * @return
         */
        public static String getKey(String tableName,String majorKey,String majorKeyValue){
            StringBuffer buffer = new StringBuffer();
            buffer.append(tableName).append(":");
            buffer.append(majorKey).append(":");
            buffer.append(majorKeyValue).append(":");
            return buffer.toString();
        }
    }
    

    如何使用?

    测试代码

    新建一个实体类:

    
    package com.domain;
    
    public class UserVo {
    
        public  static final String Table = "t_user";
    
        private String name;
        private String address;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
    
        @Override
        public String toString() {
            return "UserVo{" +
                    "name='" + name + ''' +
                    ", address='" + address + ''' +
                    ", age=" + age +
                    '}';
        }
    }
    

    再新建一个测试类:

    
    package com.config;
    
    import com.domain.UserVo;
    import com.service.RedisService;
    import com.util.RedisKeyUtil;
    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.*;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import javax.annotation.Resource;
    
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import static org.junit.Assert.*;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class RedisConfigTest {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Resource
        private ValueOperationsString,Object valueOperations;
    
        @Autowired
        private HashOperationsString, String, Object hashOperations;
    
        @Autowired
        private ListOperationsString, Object listOperations;
    
        @Autowired
        private SetOperationsString, Object setOperations;
    
        @Autowired
        private ZSetOperationsString, Object zSetOperations;
    
        @Resource
        private RedisService redisService;
    
        @Test
        public void testObj() throws Exception{
            UserVo userVo = new UserVo();
            userVo.setAddress("上海");
            userVo.setName("测试dfas");
            userVo.setAge(123);
            ValueOperationsString,Object operations = redisTemplate.opsForValue();
            redisService.expireKey("name",20, TimeUnit.SECONDS);
            String key = RedisKeyUtil.getKey(UserVo.Table,"name",userVo.getName());
            UserVo vo = (UserVo) operations.get(key);
            System.out.println(vo);
        }
    
        @Test
        public void testValueOption( )throws  Exception{
            UserVo userVo = new UserVo();
            userVo.setAddress("上海");
            userVo.setName("jantent");
            userVo.setAge(23);
            valueOperations.set("test",userVo);
    
            System.out.println(valueOperations.get("test"));
        }
    
        @Test
        public void testSetOperation() throws Exception{
            UserVo userVo = new UserVo();
            userVo.setAddress("北京");
            userVo.setName("jantent");
            userVo.setAge(23);
            UserVo auserVo = new UserVo();
            auserVo.setAddress("n柜昂周");
            auserVo.setName("antent");
            auserVo.setAge(23);
            setOperations.add("user:test",userVo,auserVo);
            SetObject result = setOperations.members("user:test");
            System.out.println(result);
        }
    
        @Test
        public void HashOperations() throws Exception{
            UserVo userVo = new UserVo();
            userVo.setAddress("北京");
            userVo.setName("jantent");
            userVo.setAge(23);
            hashOperations.put("hash:user",userVo.hashCode()+"",userVo);
            System.out.println(hashOperations.get("hash:user",userVo.hashCode()+""));
        }
    
        @Test
        public void  ListOperations() throws Exception{
            UserVo userVo = new UserVo();
            userVo.setAddress("北京");
            userVo.setName("jantent");
            userVo.setAge(23);
    //        listOperations.leftPush("list:user",userVo);
    //        System.out.println(listOperations.leftPop("list:user"));
            // pop之后 值会消失
            System.out.println(listOperations.leftPop("list:user"));
        }
    }
    

    注解缓存的使用

    @Cacheable:在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。

    @CachePut:将方法的返回值放到缓存中。

    @CacheEvict:删除缓存中的数据。

    本文代码

    https://github.com/JayTange/springbootRedis.git

    参考

    https://www.cnblogs.com/chiangchou/p/7748009.html http://www.cnblogs.com/chiangchou/p/redis-1.html

    阅****读

    1. 

    ****2. ****

    3. 

    ****4. ****

    看完本文有收获?请转发分享给更多人

    Springboot使用RedisTemplate优雅地操作redis

    原文始发于微信公众号(Java知音):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Springboot使用RedisTemplate优雅地操作redis


     上一篇
    Springboot + redis + 注解 + 拦截器来实现接口幂等性校验 Springboot + redis + 注解 + 拦截器来实现接口幂等性校验
    点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 作者:wangzaiplus www.jianshu.com/p/6189275403ed 推荐阅读(点击可跳转) 1.  ****2. **** 3. 
    下一篇 
    SpringBoot 并发登录人数控制 SpringBoot 并发登录人数控制
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:殷天文 www.jianshu.com/p/b6f5ec98d790 技术经验交流:  ,本篇示例代码放文末。通常系统都会限制同一个账号的登录人数,