Java基于springredis集成SpringBoot操作Redis

本文章为系列文章,如需系统观看请按照文章顺序阅读,如有所了解,解决针对性问题,可直接阅读,谢谢支持!

前言

Java操作Redis有很多种方式,主要有Jedis,redisson,lettuce等还有Spring家族的spring-data-redis

Jedis

是Redis的Java实现客户端,提供了比较全面的Redis命令的支持

Redisson

实现了分布式和可扩展的Java数据结构,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,延迟队列

Lettuce

用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器,主要在一些分布式缓存框架上使用比较多

spring-data-redis

一般我们都会选择 spring-data-redis

核心对象

RedisConnectionFactory

redis的连接工厂类,根据Redis配置连接上Redis服务

RedisTemplate

熟悉Spring操作JDBC都知道JdbcTemplate作用,这个类也一样,spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api

Operation

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

BoundKeyOperations

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

RedisTemplate序列化问题

介绍

当数据存储到Redis时,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer

问题

spring-data-redis中redisTemplate默认使用JDK的序列化策略,会出现两个问题

解决

spring-data-redis支持的序列化策略

spring-data-redis默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略

key和hashKey:推荐使用 StringRedisSerializer: 简单的字符串序列化

value和hashValue:推荐使用Jackson2JsonRedisSerializer

RedisTemplate和StringRedisTemplate的区别

当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择

开搞

开发环境

SpringBoot2.1.5、Redis5.0.5、Maven3.5.0、mybatis-Plus3.2.0

pom依赖

 

    org.springframework.boot
    spring-boot-starter-web




    org.springframework.boot
    spring-boot-starter-data-redis


    com.alibaba
    fastjson
    1.2.7



    org.springframework.boot
    spring-boot-starter-test
    test

application.yml文件

spring:
  #Redis配置
  redis:
    host: test201
    port: 6379
    password:
    #这些都有默认配置,如果没有特殊需求可不写
    #连接超时时间(2.0中该参数的类型为Duration,这里在配置的时候需要指明单位
    timeout: 2000ms
    #连接池最大连接数(使用负值表示没有限制)
    max-active: 8
    #连接池最大阻塞等待时间(使用负值表示没有限制)
    max-wait: -1ms
    #连接池中的最大空闲连接
    max-idle: 8
    #连接池中的最小空闲连接
    min-idle: 0

RedisConfig配置类

上边咱们说默认的RedisTemplate使用的序列化方式是JDK的,会存在特殊字符的问题而且性能损耗较大,我们创建Java Config修改默认实现的RedisTemplate序列化方式

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;

/**
 * Redis的配置类
 * SDR项目操作Redis的话需要使用RedisTemplate对象
 * 但是该对象默认使用的数据序列化方法是JDK的,可能会存在特殊字符
 *
 * key和hashKey 我们推荐使用String序列化
 * value 我们推荐是JSON存储,使用Json序列化
 */
//添加Configuration注解,否则该类不会被加载
@Configuration
public class RedisConfig {
    /**
     * @Bean:创建对象放到IOC容器中
     * RedisConnectionFactory:Redis的连接工厂,根据application.yml文件中的Redis的配置做Redis连接和连接池的管理
     该对象在项目初始化时被创建,有一个实现类是  LettuceConnectionFactory

     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){
        //创建原生的RedisTemplate对象
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);
        // 使用Jackson2JsonRedisSerialize替换 默认的JDK序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //创建JSON序列化的对象,value使用JSON序列化方式
        ObjectMapper om = new ObjectMapper();
        //设置序列化的策略
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //启用默认的类型
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //引用JSON序列化
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //创建String序列化,我们的key使用String序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用JSON
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用JSON
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //使用该配置
        redisTemplate.afterPropertiesSet();
        //返回修改后的RedisTemplate对象
        return redisTemplate;
    }
}

测试类

package com.tian.redis;

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 java.util.*;
import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Day0512RedisApplicationTests {

    @Test
    public void contextLoads() {
    }

    //注入RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 操作String
     */
    @Test
    public void test1(){
        //获取操作String'类型的对象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //存储数据
        try {
            valueOperations.set("user1","添哥");
            valueOperations.set("user2","小添哥哥");
            System.out.println("存储数据成功");
            //获取数据
            String user1 = (String)valueOperations.get("user1");
            String user2 = (String)valueOperations.get("user2");
            System.out.println(user1 + "==>" + user2);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 操作list
     */
    @Test
    public void test2(){

        ListOperations operations = redisTemplate.opsForList();

        Long num = operations.leftPushAll("l1", "l11", "l22", "l33", "l44");
        System.out.println("插入数据个数===》" + num);
        //取值
        List list = operations.range("l1", 0, -1);
        for (Object o : list) {
            System.out.println("list---》"+o);
        }

    }
    /**
     * 操作set
     */
    @Test
    public void test3(){

        SetOperations operations = redisTemplate.opsForSet();

        Long num = operations.add("s1", "s11", "s22", "s33", "s44", "s11", "s22", "s55");
        System.out.println(num);
        //获取
        Set set = operations.members("s1");
        for (Object o : set) {
            System.out.println(o);
        }
    }

    /**
     * 操作zset
     */
    @Test
    public void test4(){
        ZSetOperations operations = redisTemplate.opsForZSet();
        operations.add("z1","z10",100.00);
        operations.add("z1","z20",200.00);
        operations.add("z1","z5",50.00);
        //获取值
        Set set = operations.range("z1", 0, -1);
        for (Object o : set) {
            System.out.println(o);
        }
    }

    /**
     * 操作Hash
     */
    @Test
    public void test5(){
        HashOperations operations = redisTemplate.opsForHash();
        //存储学生1
        operations.put("stu:1:info","name","小添");
        operations.put("stu:1:info","classRoom","快刀班");
        //存储学生2
        HashMap map = new HashMap<>();
        //添加学生信息
        map.put("name","晓添");
        map.put("classRoom","吃瓜班");
        operations.putAll("stu:2:info",map);

        //获取数据1
        Object name = operations.get("stu:1:info","name");
        Object classRoom = operations.get("stu:1:info","classRoom");
        System.out.println("学生1==》"+name+"==>"+classRoom);
        //获取数据2
        Map entries = operations.entries("stu:2:info");
        //遍历map
        Set set = entries.keySet();
        for (Object o : set) {
            System.out.println(o + "==>" + entries.get(o));
        }
    }

    /**
     * 对key的操作
     */
    @Test
    public void test6(){
        //判断key是否存在
        Boolean hasKey = redisTemplate.hasKey("stu:2:info");
        System.out.println("stu:2:info===>"+hasKey);
        //设置过期时间,k3键10秒过期
        redisTemplate.expire("k3",10, TimeUnit.SECONDS);
        //设置l1键在2020 5 13 7点过期
        redisTemplate.expireAt("l1",new Date(2020,5,13,7,00,00));
    }
}
//下方两个测试是使用RedisUtil中封装使用boundHashOps方式操作
@Test
public void t1(){
   Student student = new Student();
   student.setId("100");
   student.setName("石靓仔");
   student.setSex("男");
   student.setBirth("1995-06-26");
   redisUtil.putSome(Student.class,"student",student.getId(),student);
}
@Test
public void t2(){
   List studentList = new ArrayList<>();
   Cursor> student = redisUtil.getSome("student", Student.class);
   while (student.hasNext()){
       System.out.println(student.getCursorId());
       Map.Entry next = student.next();
       Student student1 = (Student) next.getValue();
       studentList.add(student1);
   }
   for (Student student1 : studentList) {
       System.out.println(student1);
   }
}

RedisUtil类

封装一些常用的操作,大家可以根据自己的需求进行封装,这个作为参考吧

package com.tian.springbootredis.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
 * ClassName: RedisUtils
 * Description:
 * date: 2019/11/11 0011 下午 19:59
 *
 * @author stt
 * @since JDK 1.8
 */
@Component
public class RedisUtil {

    @Autowired
    public RedisTemplate redisTemplate;

    // =============================common============================
    /**
     26
     * 指定缓存失效时间
     27
     * @param key 键
    28
     * @param time 时间(秒)
    29
     * @return
    30
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据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 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public boolean del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                return redisTemplate.delete(key[0]);
            }
            return redisTemplate.delete(CollectionUtils.arrayToList(key)) > 0 ? true : false;
        }
        return false;
    }

    /**
     * 匹配所有的key
     * @param pettern
     * @return
     */
    public Set keys(String pettern){
        if(pettern.trim() != "" && pettern != null){
            return redisTemplate.keys(pettern);
        }
        return null;
    }

    // ============================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, 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, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @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 List hmget(String key) {

        return redisTemplate.opsForHash().values(key);
    }
    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map 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 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);
    }

    /**
     * 存储对象
     * @param key
     * @param field
     * @param value
     */
    public void putSome(Class cls,String key,String field,Object value){
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(cls));
        redisTemplate.boundHashOps(key).put(field,value);
    }

    /**
     * 获取对象
     * @param key
     * @param cls
     * @return
     */
    public Cursor> getSome(String key,Class cls){
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(cls));
        Cursor> scan = redisTemplate.boundHashOps(key).scan(ScanOptions.scanOptions().build());
        return scan;
    }


    // ============================set=============================
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set sGet(String key) {
        try {
            return 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, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 将set数据放入缓存
     * @param key 键
     * @param time 时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... 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 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, 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, 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 value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将list放入缓存
     *
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, 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, 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;
        }
    }
}

收藏等于学会,多多支持我吧!长期稳定输出优质系列文章!!!

页面更新:2024-03-08

标签:操作   字符串   缓存   放入   对象   策略   类型   方式   时间   数据

1 2 3 4 5

上滑加载更多 ↓
Top