SpringBoot整合Redis数据库-小白也能轻松上手-自带云Redis数据库

  • Post author:
  • Post category:其他




前言


在使用IDEA创建SpringBoot项目的时候老是初始化失败,所以我改成了阿里的: https://start.aliyun.com/


springBoot官方在 boot2.0 之后就将 jedis 改成了 lettuce


jedis:采用的是直连,多个线程操作的话,是不安全的,想要解决的话,使用 jedis pool连接池


lettuce:采用的是netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量



1.创建Boot项目

在这里插入图片描述



2.将 Added dependencies 中的都 打上勾勾

在这里插入图片描述



3.源码分析

package org.springframework.boot.autoconfigure.data.redis;


@Configuration( proxyBeanMethods = false)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    //意思是没有 name 是 redisTemplate Bean的时候才生效
    @ConditionalOnMissingBean(  name = {"redisTemplate"}  )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        //默认的 RedisTemplate 没有过多的设置,redis对象都是需要序列化!
        //两个泛型都是 object,object的类型,我们后使用需要强制转换<string,object>
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    //由于 string 是redis中最常使用的类型,所以说单独提出来了一个bean !
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

package org.springframework.boot.autoconfigure.data.redis;

import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties( prefix = "spring.redis" )
public class RedisProperties {
    // redis默认有 16个数据库 默认是使用第 0 个 
    private int database = 0;
    // ???
    private String url;
    // 链接地址 默认为本机
    private String host = "localhost";
    // 用户密码
    private String password;
    // 链接端口号
    private int port = 6379;
    // SSL加密
    private boolean ssl;
    // 超时时间 单位为 ms
    private Duration timeout;
    // 用户连接名
    private String clientName;
    // 哨兵模式
    private Sentinel sentinel;
    // 集群的配置
    private Cluster cluster;
    // boot 2.0后 弃用
    private final Jedis jedis = new Jedis();
    // boot 2.0后 采用
    private final Lettuce lettuce = new Lettuce();
}



4.配置Redis


将 application.properties 改成 application.yml

# 应用服务 WEB 访问端口
server:
  port: 8080

# 应用名称
spring:
  application:
    name: bootRedis

  # redis 由于boot版本是 2.0 以上所以不需要设置跟pool相关的 最大连接数、最小连接数什么的
  redis:
    # 云 redis地址 大家都能用 麻烦别删别人的东西
    host: 114.115.208.175
    # redis默认有 16个数据库 默认是使用第 0 个 我用第 8 个
    database: 8
    # 云 redis 没有设置密码
    password:
    # 连接名称
    clientName: "一只小狐狸OvO"
    # 超时时间 单位为 ms 我这里是3秒
    timeout: 3000



5.创建一个实体类

package com.example.bootredis.domain;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component //组件
@AllArgsConstructor //有参构造
@NoArgsConstructor//无参构造
@Data//get set
public class userDao {
    private String userCode = UUID.randomUUID().toString();
    private String userName;
    private int userAge;
}



6.创建一个存放key的接口

package com.example.bootredis.common;

/**
 * 缓存的key 常量
 *
 * @author hrrm
 */
public interface CacheConstants {

    /**
     * 用户 key
     * */
    String SYS_USER_KEY = "sys_user_tokens";

    /**
     * 用户名 key
     * */
    String SYS_USER_NAME_KEY = "sys_user_name_tokens";
    
}



7.测试往Redis中添加数据

@Autowired
private RedisTemplate redisTemplate;

@Test
void Test(){
   userDao user = new userDao();
   //opsForValue 类似String
    redisTemplate.opsForValue().set(CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode(),"一只小狐狸");
    
    //获取刚刚set进去的name 一只小狐狸
    System.out.println(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode()));
    
}


测试结果(成功):

在这里插入图片描述



8.往redis中添加对象


错误示范:

@Autowired
private RedisTemplate redisTemplate;

@Test
void Test(){
    userDao user = new userDao();
    user.setUserName("一只小狐狸OvO");
    user.setUserAge(3);
    redisTemplate.opsForValue().set(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),user);
    System.out.println(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_KEY+":"+user.getUserCode()));

}


报错如下(因为对象没有序列化):

在这里插入图片描述


解决方法1:将对象转换成 Json 格式再保存

@Test
void Test(){
    userDao user = new userDao();
    user.setUserName("一只小狐狸OvO");
    user.setUserAge(3);
    //将对象装换成JSON格式的字符串
    String JsonString = JSONObject.toJSONString(user);
    //往Redis添加值
    redisTemplate.opsForValue().set(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),JsonString);
    //从Redis中取值
    userDao userGet = JSONObject.parseObject(redisTemplate.opsForValue().get(CacheConstants.SYS_USER_KEY+":"+user.getUserCode()).toString() ,userDao.class);
    //打印结果
    System.out.println(userGet.getUserCode()+"--"+userGet.getUserName());
}

在这里插入图片描述


解决方法2:在实体类中实现序列化 再跑错误示范


在这里插入图片描述



9.修改序列化方式

**为什么需要序列化: 默认使用的JDK序列化方式,在RDM工具中查看k-v值时会出现“乱码”,不方便查看 **

在这里插入图片描述

package com.example.bootredis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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;

import java.net.UnknownHostException;

@Configuration
public class redisConfig {

    //编写自己的 redisTemplate
    @Bean
    @ConditionalOnMissingBean( name = {"myRedisTemplate"} )
    public RedisTemplate<String, Object> myRedisTemplate(RedisConnectionFactory factory)
            throws UnknownHostException {
        //为了开发方便 ,一般使用 <String,Object>
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);

        // JSON序列化 :用Json去解析任意的对象 就是说将所有对象都变成一个Json格式的字符串
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // String的所有的key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // Hash的所有的key采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value的序列化采用jackson的序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //  Hash的value序列化采用jackson的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 将所有的 Properties Set
        template.afterPropertiesSet();

        return template;
    }

}


注意:所有的包都是建在xxxApplication的同级目录下,不然spring会找不到



10.编写一个Redis的常用工具类


代码纯手敲,有可能眼花缭乱敲错了


redisTemplate .出来之后 和api的指令是一样的,建议读者自己敲一下,C位工程师太容易迷失自我了


redis中文文档:https://www.redis.net.cn/order/

package com.example.bootredis.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;

@Component
public final class RedisUtils {

    @Autowired
    @Qualifier("myRedisTemplate")
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 清除当前数据库的所有缓存
     * */
    public void removeAll(){
        Set<String> keys = redisTemplate.keys("*");
        for (String key : keys) {
            redisTemplate.delete(key);
        }
    }
    
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * */
    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 键
     * @return 时间(秒) return 0 代表永久有效 return -2 key不存在
     * */
    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 delKey(String... key){
        if (key != null &&  key.length > 0){
            if(key.length == 1){
                redisTemplate.delete(key[0]);
            }else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    //==================================String

    /**
     * String缓存放入
     * @param key 键
     * @param value 值
     * @return true 成功 false 失败
     * */
    public boolean setString(String key,String value){
        try {
            redisTemplate.opsForValue().set(key,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * String缓存获取
     * @param key 键
     * @return 值
     * */
    public Object getString(String key){
        return key == null ? null: redisTemplate.opsForValue().get(key);
    }

    /**
     * String缓存放入并设置过期时间
     * @param key 键
     * @param value 值
     * @return true 成功 false 失败
     * */
    public boolean setString(String key,Object value,long time){
        try {
            if (time > 0){
                redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
            }else { redisTemplate.opsForValue().set(key,value); }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @param key 键
     * @param delta 要递增几
     * */
    public long incr(String key,long delta){
        if(delta > 0){
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key,delta);
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几
     * */
    public long decr(String key,long delta){
        if(delta > 0){
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().decrement(key,delta);
    }

    //==================================Map

    /**
     * 获取 hashkey 中所有的值
     * @param key 键
     * @return 对应的多个键值
     * */
    public Map<Object, Object> hmget(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取 hashkey 中 对应的的值
     * @param key 键
     * @param item 项
     * @return 获取到的值
     * */
    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(key,item);
    }

    /**
     * 添加多个键值
     * @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;
        }
    }

    /**
     * 添加多个键值 并指定缓存失效时间
     * @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 添加多个对应的键值
     * */
    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 time 时间(秒)注意:如果已经存在的hash表有时间,这里将会替换为原有时间
     * @param value 添加多个对应的键值
     * */
    public boolean hset(String key,String item,long time,Object value){
        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
     * */
    public boolean hHashKey(String key,String item){
        return redisTemplate.opsForHash().hasKey(key,item);
    }

    /**
     * hash 递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @param by 要加几
     * */
    public double hincr(String key,String item,double by){
        return redisTemplate.opsForHash().increment(key,item,by);
    }

    /**
     * hash 递减
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @param by 要减几
     * */
    public double hdecr(String key,String item,double by){
        return redisTemplate.opsForHash().increment(key,item,-by);
    }

    //==================================Set

    /**
     * 根据key获取Set中所有的值
     * @param key 键 不能为null
     * */
    public Set<Object> 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 sHashKey(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 values 值 可以是多个
     * @param time 过期时间 单位为m
     * @return 成功个数
     * */
    public long sSet(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 sRemove(String key,Object... values){
        try {
            return redisTemplate.opsForSet().remove(key,values);
        }catch (Exception e){
            e.printStackTrace();
            return 0;
        }
    }

    //==================================List


    /**
     * 获取list中的缓存
     * @param key 键
     * @param state 开始
     * @param end 结束 0 到 -1 代表所有的值
     * @return 获取到的内容
     * */
    public List<Object> lGet(String key, long state, long end){
        try {
            return redisTemplate.opsForList().range(key,state,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 lGetListSize(String key,long index){
        try {
            return redisTemplate.opsForList().index(key,index);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存当中
     * @param key 键
     * @param value 值
     * @return true 成功 false 失败
     * */
    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 值
     * @return true 成功 false 失败
     * */
    public boolean lRset(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 true 成功 false 失败
     * */
    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 values 值
     * @return true 成功 false 失败
     * */
    public boolean lSetList(String key,List<Object> values){
        try {
            redisTemplate.opsForList().rightPushAll(key,values);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存当中 并设置过期时间 数组
     * @param key 键
     * @param values 值
     * @param time 时间 秒
     * @return true 成功 false 失败
     * */
    public boolean lSetList(String key,List<Object> values,long time){
        try {
            redisTemplate.opsForList().rightPushAll(key,values);
            if(time > 0){
                expire(key,time);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return true 成功 false 失败
     * */
    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;
        }
    }

}



11.在SpringBootTest当中去测试一下刚写好的工具类

@Autowired
private RedisUtils redisUtils;

@Test
void Test(){
    //清除缓存中的全部数据
    //redisUtils.removeAll();
    //创建当前对象
    UserDao user = new UserDao();
    //过期时间 秒
    long time = 20;
    //将数据存放到Hash中
    redisUtils.hset(CacheConstants.SYS_USER_KEY+":"+user.getUserCode(),CacheConstants.SYS_USER_NAME_KEY+":"+user.getUserCode(),time,"一只小狐狸OvO");
    //获取缓存剩余时间
    System.out.println("缓存剩余时间:"+redisUtils.getExpire(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode()));
    //查看缓存中的这个key的全部数据
    System.out.println("keys:"+redisUtils.hmget(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode()));
    //查看缓存中的这个key指定项的数据
    System.out.println("name:"+redisUtils.hget(CacheConstants.SYS_USER_KEY + ":" + user.getUserCode(), CacheConstants.SYS_USER_NAME_KEY + ":" + user.getUserCode()));
}


测试成功:


在这里插入图片描述


序列化之后的可视客户端:


在这里插入图片描述


SpringBoot整合Redis成功!!



版权声明:本文为My_Java_yon原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。