0
点赞
收藏
分享

微信扫一扫

02_物联网感知技术

夜空一星 04-10 08:00 阅读 2

Springboot-redis命令行封装


前言

Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSI C语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis也是现在最受欢迎的NoSQL数据库之一,其中的NoSQL是“Not Only SQL”的缩写,泛指非关系型数据库。

redis的常用使用场景,可以做缓存,分布式锁,自增序列等,使用redis的方式和我们使用数据库的方式差不多,首先我们要在自己的本机电脑或者服务器上安装一个redis的服务器,通过我们的java客户端在程序中进行集成,然后通过客户端完成对redis的增删改查操作。redis的Java客户端类型还是很多的,常见的有jedis, redission,lettuce等,所以我们在集成的时候,我们可以选择直接集成这些原生客户端。但是在springBoot中更常见的方式是集成spring-data-redis,这是spring提供的一个专门用来操作redis的项目,封装了对redis的常用操作,里边主要封装了jedis和lettuce两个客户端。相当于是在他们的基础上加了一层门面。

本篇文章我们就来重点介绍,springBoot通过集成spring-data-redis使用对于redis的常用操作。


本篇博客SpringBoot版本为2.6.13,请注意版本兼容问题

SpringBoot配置redis

一、pom文件中引入redis依赖
<!--        redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

直接引入上述依赖后,点进去会发现,里面包含了spring-data-redis和 lettuce-core两个核心包,这就是为什么说我们的spring-boot-starter-data-redis默认使用的就是lettuce这个客户端了。

如果我们想要使用jedis客户端怎么办呢?就需要排除lettuce这个依赖,再引入jedis的相关依赖就可以了。

请添加图片描述

那么为什么我们只需要通过引入不同的依赖就能让spring-data-redis可以自由切换客户端呢,这其实就涉及到了springBoot的自动化配置原理,为大家简述一下。

springBoot预设的自动化配置类都位于spring-boot-autoconfigure这个包中,只要我们搭建了springBoot的项目,这个包就会被引入进来。

请添加图片描述

这个包下就有一个RedisAutoConfiguration这个类,顾名思义就是Redis的自动化配置。在这个类中,会引入LettuceConnectionConfiguration 和 JedisConnectionConfiguration 两个配置类,分别对应lettuce和jedis两个客户端。

请添加图片描述

LettuceConnectionConfiguration配置类中,通过@ConditionalOnClass 和 ConditionalOnProperty进行条件判断,是否允许自动装配。

请添加图片描述

JedisConnectionConfiguration 中也通过类似的条件判断注解进行判定是否自动装配

请添加图片描述

由于我们的项目通过redis starter 自动引入了lettuce-core,而没有引入jedis相关依赖,所以LettuceConnectionConfiguration这个类的判断成立会被加载,而Jedis的判断不成立,所以不会加载。进而lettuce的配置生效,所以我们在使用的使用, 默认就是lettuce的客户端。

二、yml文件中进行基本的配置
spring:
  redis:
    host: localhost
    password:   #your password
    port: 6379
    database: 0 #default database 0
    #连接池
    lettuce:
      pool:
        max-active: 8 #最大连接池
        max-idle: 4 #连接池中的最大空闲连接
        min-idle: 2 #连接池中的最小空闲连接

但是有的时候我们想要给我们的redis客户端配置上连接池。就像我们连接mysql的时候,也会配置连接池一样,目的就是增加对于数据连接的管理,提升访问的效率,也保证了对资源的合理利用。那么我们如何配置连接池呢,这里大家一定要注意了,很多网上的文章中,介绍的方法可能由于版本太低,都不是特别的准确。 比如很多人使用spring.redis.pool来配置,这个是不对的(不清楚是不是老版本是这样的配置的,但是在springboot-starter-data-redis中这种写法不对)。首先是配置文件,由于我们使用的lettuce客户端,所以配置的时候,在spring.redis下加上lettuce再加上pool来配置。

此外连接池配置还需要加入一个依赖

 <!--        对象池依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
三、项目中使用

我们的配置工作准备就绪以后,我们就可以在项目中操作redis了,操作的话,使用spring-data-redis中为我们提供的 RedisTemplate 这个类,就可以操作了。我们先举个简单的例子,插入一个键值对(值为string)

@RestController
@RequestMapping("/redis")
public class BasicController {

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("save")
    public String save(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
        return "ok";
    }
}

但是运行之后发送请求,在成功存储后,通过命令查看redis 数据库0,获取keys的时候,会发现一个比较难受的东西,我们发现存进去的key明明是"a",但是查询得到的key确是这么一段玩意儿,而且通过它获取value也不会获取成功。

问题出在了这里:

那就自定义一下序列化叭

package com.jerry.springbootredis.conf;

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;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 14:32
 * @注释
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 创建RedisTemplate<String, Object>对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 定义Jackson2JsonRedisSerializer序列化对象
        Jackson2JsonRedisSerializer<Object> 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);
        StringRedisSerializer stringSerial = new StringRedisSerializer();
        // redis key 序列化方式使用stringSerial
        template.setKeySerializer(stringSerial);
        // redis value 序列化方式使用jackson
        template.setValueSerializer(jacksonSeial);
        // redis hash key 序列化方式使用stringSerial
        template.setHashKeySerializer(stringSerial);
        // redis hash value 序列化方式使用jackson
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();
        return template;
    }
}

再次通过api接口保存(“a”,“b”),通过可视化工具查看,获得a的值为”b“

请添加图片描述

四、工具类封装

我们在前面的代码中已经通过RedisTemplate成功操作了redis服务器,比如set一个字符串,我们可以使用:

redisTemplate.opsForValue().set(key, value);

来put一个String类型的键值对。而redis中可以支持 string, list, hash,set, zset五种数据格式,这五种数据格式的常用操作,都在RedisTemplate这个类中进行了封装。 操作string类型就是用opsForValue,操作list类型是用listOps, 操作set类型是用setOps等等。

尝试通过自定义工具类的方式进行一些操作的封装,在之后的操作中可以直接注入工具类,进行使用

封装:
package com.jerry.springbootredis.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 14:42
 * @注释 封装redis 操作命令
 */
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate redisTemplate;

    /***
     * 为键所对应的值设置过期时间
     * @param key
     * @param timeout 毫秒数
     * @return
     */
    public boolean expire(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);
    }

    /***
     * 根据key 获取键值对过期时间
     * @param key
     * @return
     */
    public long getValueTimeOut(String key) {
        return redisTemplate.getExpire(key);
    }

    /***
     * 查找是否包含某个键
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /***
     * 移除指定某个key的时间
     * @param key
     * @return
     */
    public boolean persist(String key) {
        return redisTemplate.boundValueOps(key).persist();
    }

    //------------------String 操作--------------

    /***
     * 按照key值取
     * @param key
     * @return
     */
    public Object get(String key) {
        return key.isEmpty() ? null : redisTemplate.opsForValue().get(key);
    }

    /***
     * 按照key进行存
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /***
     * 按照key设置过期时间存
     * @param key
     * @param value
     * @param timeOut ms
     */
    public void set(String key, String value, Long timeOut) {
        if (timeOut > 0) {
            redisTemplate.opsForValue().set(key, value, timeOut, TimeUnit.MILLISECONDS);
        } else {
            redisTemplate.opsForValue().set(key, value);
        }
    }

    /**
     * 批量添加 key (重复的键会覆盖)
     *
     * @param keyAndValue
     */
    public void batchSet(Map<String, String> keyAndValue) {
        redisTemplate.opsForValue().multiSet(keyAndValue);
    }

    /**
     * 批量添加 key-value 只有在键不存在时,才添加
     * map 中只要有一个key存在,则全部不添加
     *
     * @param keyAndValue
     */
    public void batchSetIfAbsent(Map<String, String> keyAndValue) {
        redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);
    }

    /**
     * 对一个 key-value 的值进行加减操作,
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是长整型 ,将报错
     *
     * @param key
     * @param number
     */
    public Long increment(String key, long number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    /**
     * 对一个 key-value 的值进行加减操作,
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是 纯数字 ,将报错
     *
     * @param key
     * @param number
     */
    public Double increment(String key, double number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    //------------------无序集合 set类型 操作--------------

    /**
     * 将数据放入set缓存
     *
     * @param key 键
     * @return
     */
    public void sSet(String key, String value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取变量中的值
     *
     * @param key 键
     * @return
     */
    public Set<Object> members(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取set中key变量中指定个数的value
     *
     * @param key   键
     * @param count 个数
     * @return key=key key对应的value值中随机取count个返回
     */
    public List randomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取变量中的元素
     *
     * @param key 键
     * @return key=key key对应的value值中随机取1个返回
     */
    public Object randomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 弹出无序列表中的元素
     * 全部弹出后列表会被删除,继续弹出会报错空指针
     * @param key 键
     * @return
     */
    public Object pop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 获取变量中值的长度
     *
     * @param key 键
     * @return
     */
    public long size(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 检查给定的元素是否在变量中。
     *
     * @param key 键
     * @param obj 元素对象
     * @return
     */
    public boolean isMember(String key, Object obj) {
        return redisTemplate.opsForSet().isMember(key, obj);
    }

    /**
     * 转移变量的元素值到目的变量。
     *
     * @param key     键
     * @param value   元素对象
     * @param destKey 元素对象
     * @return
     */
    public boolean move(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 批量移除set缓存中元素
     *
     * @param key    键
     * @param values 值
     * @return
     */
    public void remove(String key, Object... values) {
        redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 通过给定的key求2个set变量的差值
     *
     * @param key     键
     * @param destKey 键
     * @return
     */
    public Set<Set> difference(String key, String destKey) {
        return redisTemplate.opsForSet().difference(key, destKey);
    }


    ------------------hash类型 操作--------------

    /**
     * 加入缓存
     *
     * @param key 键
     * @param map 键
     * @return
     */
    public void add(String key, Map<String, String> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取 key 下的 所有  hashkey 和 value
     *
     * @param key 键
     * @return
     */
    public Map<Object, Object> getHashEntries(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 验证指定 key 下 有没有指定的 hashkey
     *
     * @param key
     * @param hashKey
     * @return
     */
    public boolean hashKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * 获取指定key的值string
     *
     * @param key  redis的键
     * @param key2 存储数据map的键
     * @return
     */
    public String getMapString(String key, String key2) {
        return redisTemplate.opsForHash().get(key, key2).toString();
    }

    /**
     * 获取指定的值Int
     *
     * @param key  redis的键
     * @param key2 存储数据map的键
     * @return map中可以转化为int的value值
     */
    public Integer getMapInt(String key, String key2) {
        return Integer.valueOf( redisTemplate.opsForHash().get(key, key2).toString());
    }

    /**
     * 弹出元素并删除
     *
     * @param key 键
     * @return
     */
    public String popValue(String key) {
        return redisTemplate.opsForSet().pop(key).toString();
    }

    /**
     * 删除指定 hash 的 HashKey
     *
     * @param key
     * @param hashKeys
     * @return 删除成功的 数量
     */
    public Long delete(String key, String... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public Long increment(String key, String hashKey, long number) {
        return redisTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public Double increment(String key, String hashKey, Double number) {
        return redisTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 获取 key 下的 所有 hashkey 字段
     *
     * @param key
     * @return
     */
    public Set<Object> hashKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取指定 hash 下面的 键值对 数量
     *
     * @param key
     * @return
     */
    public Long hashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    //---------------------list类型---------------------

    /**
     * 在变量左边添加元素值
     *
     * @param key
     * @param value
     * @return
     */
    public void leftPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 获取集合指定位置的值。
     *
     * @param key
     * @param index
     * @return
     */
    public Object index(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 获取指定区间的值。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<Object> range(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 把最后一个参数值放到指定集合的第一个出现中间参数的前面,
     * 如果中间参数值存在的话。
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public void leftPush(String key, String pivot, String value) {
        redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 向左边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public void leftPushAll(String key, String... values) {
        redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 向集合最右边添加元素。
     *
     * @param key
     * @param value
     * @return
     */
    public void leftPushAll(String key, String value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 向左边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public void rightPushAll(String key, String... values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 向已存在的集合中添加元素。
     *
     * @param key
     * @param value
     * @return
     */
    public void rightPushIfPresent(String key, Object value) {
        redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 向已存在的集合中添加元素。
     *
     * @param key
     * @return
     */
    public long listLength(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 移除集合中的左边第一个元素。
     *
     * @param key
     * @return
     */
    public void leftPop(String key) {
        redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移除集合中左边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
     *
     * @param key
     * @return
     */
    public void leftPop(String key, long timeout, TimeUnit unit) {
        redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除集合中右边的元素。
     *
     * @param key
     * @return
     */
    public void rightPop(String key) {
        redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。
     *
     * @param key
     * @return
     */
    public void rightPop(String key, long timeout, TimeUnit unit) {
        redisTemplate.opsForList().rightPop(key, timeout, unit);
    }
}

使用:
package com.jerry.springbootredis.demos;

import com.jerry.springbootredis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("/redis")
public class BasicController {

    @Autowired
    private RedisUtils redisUtils;

//    @GetMapping("save")
//    public String save(String key, String value) {
//        redisTemplate.opsForValue().set(key, value);
//        return "ok";
//    }

    @GetMapping("sMap")
    public String sMap(String key, String hashKey, String hashVal) {
        Map<String, String> map = new HashMap<>();
        map.put(hashKey, hashVal);
        redisUtils.add(key, map);

        return "ok";

    }

    @GetMapping("getMapString")
    public String getMapString(String key, String hasKey) {
        String mapString = redisUtils.getMapString(key, hasKey);
        return mapString;
    }

    @GetMapping("getMapInt")
    public String getMapInt(String key, String hasKey) {
        Integer mapInt = redisUtils.getMapInt(key, hasKey);
        return mapInt.toString();
    }
    //.....
}


代码demo地址github🤖


鸣谢:稀土掘金:一缕82年的清风(SpringBoot教程(十四) | SpringBoot集成Redis(全网最全))


总结

以上就是Redis的命令行基本命令介绍,希望对你有所帮助。如果想了解更多关于Redis的内容,可以参考Redis官方文档。

举报

相关推荐

0 条评论