【Redis】Redis 的学习教程(七)之 SpringBoot 集成 Redis

news2024/11/26 4:42:16

在前几篇文章中,我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。

在当前流行的微服务以及分布式集群环境下,Redis 的使用场景可以说非常的广泛,能解决集群环境下系统中遇到的不少技术问题,在此列举几个使用 Redis 经常用到的功能:

  • 分布式缓存:在分布式的集群架构中,将缓存存储在内存中会出现很多的问题,比如用户回话信息,因为这部分信息需要与其他机器共享,此时利用 Redis 可以很好的解决机器之间数据共享的问题,缓存也是 Redis 中使用最多的场景
  • 分布式锁:在高并发的情况下,我们需要一个锁来防止并发带来的脏数据,Java 自带的锁机制显然对进程间的并发并不好使,此时利用 Redis 的单线程特性,实现分布式锁控制
  • 接口限流:在集群环境下,可以利用 Redis 的分布式自增 ID 功能,精准的统计每个接口在指定时间内的请求次数,利用这个特性,可以定向限制某个接口恶意频刷

当然 Redis 的使用场景并不仅仅只有这么多,还有很多未列出的场景,如发布/订阅,分布锁集合等。

现实中我们大部分的微服务项目,都是基于 SpringBoot 框架进行快速开发,在 SpringBoot 项目中我们应该如何使用 Redis 呢?代码实践如下。

1. 开发环境

  • IDEA:2021.3.3
  • JDK:1.8
  • SpringBoot:2.7.14
  • Maven:3.6.3

咱们通过程序是不能直接连接 Redis,得利用客户端工具才能进行连接。比较常用的有两种:Jedis、Lettuce。

在 springboot 1.5.x 版本的默认的 Redis 客户端是 Jedis 实现的,springboot 2.x 版本中默认客户端是用 lettuce实现的。

既然 LettuceJedis 的都是连接 Redis 的客户端,那么它们有什么区别呢?

  • Jedis 在实现上是直连 Redis Server,多线程环境下非线程安全,除非使用连接池,为每个 Redis 实例增加 物理连接
  • Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个 RedisConnection,它利用 Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序

2. 代码实战

在 SpringBoot 集成的 Redis 时,我这里采用的是 Lettuce

2.1 默认使用 Lettuce

1、引入依赖:

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

2、添加配置:

spring:
  redis:
    host: localhost
    port: 6379
    password:
    timeout: 2000s
    # 配置文件中添加 lettuce.pool 相关配置,则会使用到lettuce连接池
    lettuce:
      pool:
        max-active: 8  # 连接池最大连接数(使用负值表示没有限制) 默认为8
        max-wait: -1ms # 接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1ms
        max-idle: 8    # 连接池中的最大空闲连接 默认为8
        min-idle: 0    # 连接池中的最小空闲连接 默认为 0

2.2 换成 Jedis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

jedis 中会引入 commons-pool2 依赖,如果没有引入:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

添加配置:

spring:
  redis:
    host: localhost
    port: 6379
    password:
    timeout: 2000s
    # 配置文件中添加 jedis.pool 相关配置,则会使用到 jedis 连接池
    jedis:
      pool:
        max-active: 10
        max-idle: 8
        min-idle: 0
        max-wait: 60s

2.3 使用 RedisTemplate 对象操作 Redis

在 SpringBoot 中,是使用 RedisTemplate 对象来操作 Redis 的。

在 Springboot 自动配置原理中,涉及到以下两方面:

  1. SpringBoot 中所有的配置类,都有一个自动配置类。RedisAutoConfiguration
  2. 自动配置类都会绑定一个配置文件 properties。RedisProperties

RedisAutoConfiguration.class

public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

StringRedisTemplate

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

通过上述看,注入了两个类型的 RedisTemplate 对象:

  1. 如果没有注入名称为 redisTemplate 的 RedisTemplate 对象,则注入 RedisTemplate<Object, Object> 对象
  2. 注入 StringRedisTemplate 对象。而 StringRedisTemplate 对象又是继承 RedisTemplate<String, String> 类的

使用上面两个类型 RedisTemplate 的对象操作 Redis:

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

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @GetMapping("/set")
    public String set() {
        stringRedisTemplate.opsForValue().set("name", "zzc");
        redisTemplate.opsForValue().set("age", "zzc");
        return "set";
    }
}

调用成功后,我们使用 Redis 客户端工具进行查看:

在这里插入图片描述
发现:

redisTemplate.opsForValue().set("age", "zzc"); 操作的 keyvalue 都变成乱码。

springboot系列——redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较

通过 debug 源代码知:RedisTemplate<Object, Object> 的 key、value 序列化默认都是 JdkSerializationRedisSerializer,序列化方法如下:

default byte[] serializeToByteArray(T object) throws IOException {
   ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
   this.serialize(object, out);
   return out.toByteArray();
}

public void serialize(Object object, OutputStream outputStream) throws IOException {
    if (!(object instanceof Serializable)) {
        throw new IllegalArgumentException(this.getClass().getSimpleName() + " requires a Serializable payload but received an object of type [" + object.getClass().getName() + "]");
    } else {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
    }
}

将 key、value 进行序列化成 byte 类型,所以,看上去会乱码。(可读性差)

StringRedisTemplate 对象使用 RedisSerializer 序列的

2.4 自定义 RedisTemplate 对象

为了可读性,可以使用 StringRedisTemplate 类,但有一个要求:key、value 都要求是 String 类型。

但这就有一个问题,我们平时用得对象比较多,那又如何存储对象呢?

例如,我们这里的 User 对象:

public class User {
    private String id;
    private String userName;
    private Integer age;
    // getter/setter
}

由于 RedisTemplate<String, String> 的泛型参数都是 String 类型的,那我们只需要将 Java 对象转换为 String 对象即可:

@Override
public boolean addUser(User user) {
   redisTemplate.opsForValue().set("user", JSON.toJSONString(user));
   String strUser = redisTemplate.opsForValue().get("user1");
   User resultUser = JSON.parseObject(strUser, User.class);
   return true;
}

存 Redis 之前,将 Java 对象转换为 Json 字符串;读取后,将 Json 字符串转换为 Java 对象。

这样做确实可行,但是,如果要存储的对象较多的话,那岂不是要重复地将 Java 对象转换为 Json 字符串?这样是不是很繁琐?

继续看 RedisAutoConfiguration.class 源码,发现被注入的 RedisTemplate<Object, Object>@ConditionalOnMissingBean(name="redisTemplate") 注解修饰:如果 Spring 容器中有了 RedisTemplate 对象了,这个自动配置的 RedisTemplate 不会实例化。因此我们可以直接自己写个配置类,配置 RedisTemplate。并且,我们更希望 key 是 String 类型,value 是 Object 类型(String、int、对象等类型):

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // json 序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 所有的 key 采用 string 的序列化
        template.setKeySerializer(stringRedisSerializer);
        // 所有的 value 采用 jackson 的序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的 key 采用 string 的序列化
        template.setHashKeySerializer(stringRedisSerializer);
        // hash 的 value 采用 jackson 的序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

测试:

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

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/set")
    public String set() {
        User user = new User();
        user.setName("zzc");
        user.setAge(18);
        redisTemplate.opsForValue().set("user", user);
        return "set";
    }

}

2.5 RedisUtil 工具类

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    // ============================Common=============================
    
    public void setHashValueSerializer(RedisSerializer serializer) {
        redisTemplate.setHashValueSerializer(serializer);
    }

    /**
     * 指定缓存失效时间
     *
     * @author zzc
     * @date 2023/8/2 11:06
     * @param key    键
     * @param time   时间(秒)
     * @return boolean
     */
    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 获取过期时间
     *
     * @author zzc
     * @date 2023/8/2 11:07
     * @param key    键 不能为null
     * @return long  时间(秒) 返回0代表为永久有效
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @author zzc
     * @date 2023/8/2 11:07
     * @param key      键
     * @return boolean 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @author zzc
     * @date 2023/8/2 11:08
     * @param key   可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                //springboot2.4后用法
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    /**
     * 获取指定前缀的一系列key
     * 使用scan命令代替keys, Redis是单线程处理,keys命令在KEY数量较多时,
     * 操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求
     *
     * @author zzc
     * @date 2023/8/2 11:53
     * @param keyPrefix
     * @return java.util.Set<java.lang.String>
     */
    public Set<String> keys(String keyPrefix) {
        String realKey = keyPrefix + "*";
        try {
            return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
                Set<String> binaryKeys = new HashSet<>();
                //springboot2.4后用法
                Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(realKey).count(Integer.MAX_VALUE).build());
                while (cursor.hasNext()) {
                    binaryKeys.add(new String(cursor.next()));
                }

                return binaryKeys;
            });
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 删除指定前缀的一系列key
     *
     * @author zzc
     * @date 2023/8/2 11:53
     * @param keyPrefix
     */
    public void removeAll(String keyPrefix) {
        try {
            Set<String> keys = keys(keyPrefix);
            redisTemplate.delete(keys);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    
    // 执行 lua 脚本
    public <T> T execute(RedisScript<T> script, List<String> keys, Object... args) {
        return redisTemplate.execute(script, keys, args);
    }

    public boolean convertAndSend(String channel, Object message) {
        if (!StringUtils.hasText(channel)) {
            return false;
        }
        try {
            redisTemplate.convertAndSend(channel, message);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


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

    /**
     * 普通缓存获取
     *
     * @author zzc
     * @date 2023/8/2 11:08
     * @param key                   键
     * @return java.lang.Object     值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @author zzc
     * @date 2023/8/2 11:09
     * @param key           键
     * @param value         值
     * @return boolean      true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置过期时间
     *
     * @author zzc
     * @date 2023/8/2 11:09
     * @param key     键
     * @param value   值
     * @param time    时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return boolean  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;
        }
    }

    /**
     * 递增
     * @author zzc
     * @date 2023/8/2 11:10
     * @param key         键
     * @param delta       要增加几(大于0)
     * @return java.lang.Long
     */
    public Long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @author zzc
     * @date 2023/8/2 11:11
     * @param key                 键
     * @param delta               要减少几(小于0)
     * @return java.lang.Long
     */
    public Long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    public boolean setNx(String key, Object value) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));
    }
    
    public boolean setNx(String key, Object value, long time) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS));
    }
    
    public void multiSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    public List<Object> multiGet(List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }


    // ================================Hash=================================

    /**
     * Hash Get
     * @author zzc
     * @date 2023/8/2 11:12
     * @param key                键 不能为null
     * @param item               项 不能为null
     * @return java.lang.Object
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取Key对应的所有键值
     *
     * @author zzc
     * @date 2023/8/2 11:12
     * @param key                                                  键
     * @return java.util.Map<java.lang.Object,java.lang.Object>    对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * Hash Set
     *
     * @author zzc
     * @date 2023/8/2 11:13
     * @param key         键
     * @param map         对应多个键值
     * @return boolean    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;
        }
    }

    /**
     * Hash Set 并设置过期时间
     * @author zzc
     * @date 2023/8/2 11:13
     * @param key        键
     * @param map        对应多个键值
     * @param time       时间(秒)
     * @return boolean   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表中的项
     *
     * @author zzc
     * @date 2023/8/2 11:38
     * @param key   键 不能为null
     * @param item  项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断 hash 表中是否有该项的值
     *
     * @author zzc
     * @date 2023/8/2 11:38
     * @param key    键 不能为null
     * @param item   项 不能为null
     * @return boolean  true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash 递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @author zzc
     * @date 2023/8/2 11:40
     * @param key    键
     * @param item   项
     * @param by     要增加几(大于0)
     * @return double
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    public Long hincr(String key, String item, long by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash 递减
     *
     * @author zzc
     * @date 2023/8/2 11:40
     * @param key    键
     * @param item   项
     * @param by     要减少几(小于0)
     * @return double
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    public List<Object> hmultiGet(String key, List<Object> items) {
        return redisTemplate.opsForHash().multiGet(key, items);
    }


    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @author zzc
     * @date 2023/8/2 11:41
     * @param key
     * @return java.util.Set<java.lang.Object>
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @author zzc
     * @date 2023/8/2 11:41
     * @param key       键
     * @param value     值
     * @return boolean  true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @author zzc
     * @date 2023/8/2 11:42
     * @param key       键
     * @param values    值 可以是多个
     * @return long     成功个数
     */
    public Long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 将 set 数据放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:42
     * @param key     键
     * @param time    时间(秒)
     * @param values  值 可以是多个
     * @return long   成功个数
     */
    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 0L;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @author zzc
     * @date 2023/8/2 11:45
     * @param key
     * @return long
     */
    public Long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 移除值为value的
     *
     * @author zzc
     * @date 2023/8/2 11:45
     * @param key    键
     * @param values 值 可以是多个
     * @return long  移除的个数
     */
    public Long setRemove(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }


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

    /**
     * 获取list缓存的内容
     *
     * @author zzc
     * @date 2023/8/2 11:46
     * @param key      键
     * @param start    开始
     * @param end      结束 0 到 -1代表所有值
     * @return java.util.List<java.lang.Object>
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @author zzc
     * @date 2023/8/2 11:47
     * @param key
     * @return long
     */
    public Long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @author zzc
     * @date 2023/8/2 11:47
     * @param key     键
     * @param index   索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return java.lang.Object
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @author zzc
     * @date 2023/8/2 11:48
     * @param key       键
     * @param value     值
     * @return boolean
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @author zzc
     * @date 2023/8/2 11:48
     * @param key       键
     * @param value     值
     * @param time  时间(秒)
     * @return boolean
     */
    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放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:49
     * @param key        键
     * @param value      值
     * @return boolean   时间(秒)
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @author zzc
     * @date 2023/8/2 11:49
     * @param key        键
     * @param value      值
     * @param time       时间(秒)
     * @return boolean
     */
    public boolean lSet(String key, List<Object> 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中的某条数据
     *
     * @author zzc
     * @date 2023/8/2 11:51
     * @param key     键
     * @param index   索引
     * @param value   值
     * @return boolean
     */
    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
     *
     * @author zzc
     * @date 2023/8/2 11:51
     * @param key    键
     * @param count  移除多少个
     * @param value  值
     * @return long  移除的个数
     */
    public Long lRemove(String key, long count, Object value) {
        try {
            return redisTemplate.opsForList().remove(key, count, value);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/953097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++信息学奥赛1182:合影效果

#include <bits/stdc.h> using namespace std;int main() {int n; // 人数cin >> n;string arr[n]; // 存储性别的数组double brr[n]; // 存储身高的数组// 读取每个人的性别和身高for (int i 0; i < n; i){cin>>arr[i]>>brr[i];}// 对男…

JavaScript Web APIs - 06 正则表达式

Web APIs - 06 文章目录 Web APIs - 06正则表达式正则基本使用元字符边界符量词范围字符类 替换和修饰符正则插件change 事件判断是否有类 目标&#xff1a;能够利用正则表达式完成小兔鲜注册页面的表单验证&#xff0c;具备常见的表单验证能力 正则表达式综合案例阶段案例 正…

Kafka系列一消息队列

文章首发于个人博客&#xff0c;欢迎访问关注&#xff1a;https://www.lin2j.tech 消息队列是分布式系统中的重要组件&#xff0c;也是 Java 开发中常用的技术点之一。 使用消息队列可以解决模块间的解耦、流量削峰、异步消息&#xff0c;提高系统的可用性、稳定性以及性能。…

Python编程——深入了解不可变的元组

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一、元组是什么 二、元组的定义 1、相同类型组成元组…

如何利用 SmartX 存储性能测试工具 OWL 优化性能管理?

作者&#xff1a;深耕行业的 SmartX 金融团队 张瑞松 运维人员在日常管理集群时&#xff0c;有时难免会产生这样的困惑&#xff1a; 新业务准备上线&#xff0c;在具备多套存储的情况下&#xff0c;应如何选择承载业务的存储环境&#xff1f; 业务虚拟机刚上线时运行速度很快…

<algorithm> 头文件

头文件是 C 标准库中的一个头文件&#xff0c;提供了许多常用的算法函数。除了 erase 之外&#xff0c; 头文件还包含了以下一些重要的函数&#xff1a; 1、排序算法&#xff1a; sort&#xff1a;对容器进行排序。 #include <iostream> #include <algorithm> #…

基于Android的旅游管理系统 微信小程序

随着网络科技的发展&#xff0c;移动智能终端逐渐走进人们的视线&#xff0c;相关应用越来越广泛&#xff0c;并在人们的日常生活中扮演着越来越重要的角色。因此&#xff0c;关键应用程序的开发成为影响移动智能终端普及的重要因素&#xff0c;设计并开发实用、方便的应用程序…

百度文心一率先言向全社会开放 应用商店搜“文心一言”可直接下载

8月31日&#xff0c;文心一言率先向全社会全面开放。广大用户可以在应用商店下载“文心一言APP”或登陆“文心一言官网”&#xff08;https://yiyan.baidu.com&#xff09; 体验。同时&#xff0c;企业用户可以直接登录百度智能云千帆大模型平台官网&#xff0c;调用文心一言能…

IntelliJ IDEA中代码提示功能不见了

IntelliJ IDEA中代码提示功能不见了&#xff1f;试试这几个方法&#xff01; 在我们敲代码的时候&#xff0c;环境中的代码提示功能非常强大且方便。但是这突如其来的问题纠结我两天&#xff0c;不给提示&#xff0c;不给引包&#xff0c;连个错他妹的也不给报。 第一种情况&…

Texlive2023与Texstudio2023卸载与安装(详细全程)

早在两年前安装了texlive2020&#xff0c;最近重新使用总是报错&#xff0c;好像是因为版本过低。我就找了个时间更新一下texlive版本&#xff0c;全程如下。 1、卸载texlive老版本 1&#xff09;找到texlive目录&#xff0c;比如我的是D:\texlive\2022\tlpkg\installer&…

[linux实战] 华为云耀云服务器L实例 Java、node环境配置

系列文章目录 第一章 [linux实战] 华为云耀云服务器L实例 Java、node环境配置 文章目录 系列文章目录前言一、任务拆解二、修改密码三、配置安全规则四、远程登录并更新apt五、安装、配置JDK环境5.1、安装openjdk,选择8版本5.2、检查jdk配置 六、安装、配置git6.1、安装git6.2…

一些git心得:建仓库的最好方式是克隆;每个仓库的设置是比较独立的

背景&#xff1a;最近&#xff0c;我在gitee上新建了一个仓库&#xff0c;然后克隆下来后&#xff0c;发现远程配置竟然自动都配好了。于是&#xff0c;在git的使用上有一了点点新的体会。 一、每个仓库是互相独立的&#xff0c;包括一些设置。 比如上面是两个仓库&#xff0c…

【App出海成功案例】 | NetMarvel 帮助广告主ARPU增长45%,ECPM增长50%,付费率涨幅30%

中国App何以扬帆出海&#xff1f; 出海热发展到今天&#xff0c;中国App席卷西方世界的神话被一一打造&#xff0c;手游/非游双面开花&#xff0c;成功案例作为赛道代表&#xff0c;也成为众多出海广告主一一效仿的风向标。 它们在用户增长、变现收益上的打法是怎样的&#x…

Unity 之 参数类型之值类型参数的用法

文章目录 基本数据类型结构体结构体的进一步补充 总结&#xff1a; 当谈论值类型参数时&#xff0c;我们可以从基本数据类型和结构体两个方面详细解释。值类型参数指的是以值的形式传递给函数或方法的数据&#xff0c;而不是引用。 基本数据类型 基本数据类型的值类型参数&…

Spring Cloud 快速入门基本介绍

&#x1f600;前言 本篇博文是关于Spring Cloud 基本介绍&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x…

3个微信恢复聊天记录的方法,简单易懂

朋友们&#xff0c;我要被自己气死了。手欠删除了微信上的所有聊天对话框&#xff0c;我发誓我真的不知道这样做会清空聊天记录&#xff0c;有什么办法能恢复我的聊天记录吗&#xff1f; 是不是还有很多小伙伴都不知道微信这个令人害怕的操作&#xff1a;删除聊天对话框删除聊天…

MySql刷题知识汇总

1.CHAR_LENGTH &#xff1a; 求varchar类型的长度。

TCP之三次握手四次挥手

在前面的文章中我们了解到http是基于TCP/IP协议的&#xff0c;这篇文章我们来了解一下TCP/IP。 一、TCP与UDP 1、UDP 基于非连接。类似于写信&#xff0c;不能保证对方能不能接收到&#xff0c;接收到的内容是否完整&#xff0c;顺序是否正确。 优缺点&#xff1a;性能损耗小…

【FlowDroid】一、处理流程学习

FlowDroid 一、处理流程学习 下载配置源码概况代码逻辑分析analyzeAPKFilerunInfoflowprocessEntryPointcalculateCallbacks(sourcesAndSinks)再次回到processEntryPoint 自己做一些笔记 下载配置 参照我前面的文章可以使用FlowDroid安装初体验 为了看代码了解FlowDroid如何处…

关于CICD流水线的前端项目运行错误,npm项目环境配置时出现报错:Not Found - GET https://registry.npm...

关于CICD流水线的前端项目运行错误&#xff0c;npm项目环境配置时出现报错&#xff1a;Not Found - GET https://registry.npm… 原因应该是某些jar包缓存中没有需要改变镜像将包拉下来 npm config set registry http://registry.npm.taobao.org npm install npm run build