Redis - 一篇走心的 RedisUtil 工具类
Posted 程序员牧码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis - 一篇走心的 RedisUtil 工具类相关的知识,希望对你有一定的参考价值。
声明:此工具类原型参考
自https://github.com/whvcse/RedisUtil,并作了进一步进行整理、修正、补充、拓展。
此 RedisUtil 工具类的功能
直接使用此 RedisUtil
工具类的所需条件
- 项目基于SpringBoot
- pom.xml中需要引入依赖spring-boot-starter-data-redis
- pom.xml中需要引入依赖lombok
- pom.xml中需要引入依赖fastjson
注:其中第3、4点不是必须的,在此工具类中,第3、4点只是为了方便记录日志而已。
注:当然,如果是非SpringBoot的项目,那么也可以通过xml配置等方式,来使用此工具类,自行摸索吧。
编写测试此 RedisUtil
工具类时,项目结构说明(可跳过)
提示:如果只是使用此工具类的话,可以不看这个,直接将下面的工具类复制到SpringBoot项目中即可;如果想要进一步了解此工具类、了解StringRedisTemplate的api的话,可以将本人放在github上的源码(跳转链接见文末)拽下来,自行测试。
RedisUtil
工具类
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
*
* 声明: 此工具只简单包装了redisTemplate的大部分常用的api, 没有包装redisTemplate所有的api。
* 如果对此工具类中的功能不太满意, 或对StringRedisTemplate提供的api不太满意,
* 那么可自行实现相应的{@link StringRedisTemplate}类中的对应execute方法, 以达
* 到自己想要的效果; 至于如何实现,则可参考源码或{@link RedisUtil.LockOps}中的方法。
*
* 注: 此工具类依赖spring-boot-starter-data-redis类库、以及可选的lombok、fastjson
* 注: 更多javadoc细节,可详见{@link RedisOperations}
*
* 统一说明一: 方法中的key、 value都不能为null。
* 统一说明二: 不能跨数据类型进行操作, 否者会操作失败/操作报错。
* 如: 向一个String类型的做Hash操作,会失败/报错......等等
*
* @author JustryDeng
* @date 2020/3/7 16:50:05
*/
@Slf4j
@Component
@SuppressWarnings("unused")
public class RedisUtil implements ApplicationContextAware {
/** 使用StringRedisTemplate(,其是RedisTemplate的定制化升级) */
private static StringRedisTemplate redisTemplate;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RedisUtil.redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
}
/**
* key相关操作
*
* @author JustryDeng
* @date 2020/3/7 16:54:25
*/
public static class KeyOps {
/**
* 根据key, 删除redis中的对应key-value
*
* 注: 若删除失败, 则返回false。
*
* 若redis中,不存在该key, 那么返回的也是false。
* 所以,不能因为返回了false,就认为redis中一定还存
* 在该key对应的key-value。
*
* @param key
* 要删除的key
* @return 删除是否成功
* @date 2020/3/7 17:15:02
*/
public static boolean delete(String key) {
log.info("delete(...) => key -> {}", key);
// 返回值只可能为true/false, 不可能为null
Boolean result = redisTemplate.delete(key);
log.info("delete(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 根据keys, 批量删除key-value
*
* 注: 若redis中,不存在对应的key, 那么计数不会加1, 即:
* redis中存在的key-value里,有名为a1、a2的key,
* 删除时,传的集合是a1、a2、a3,那么返回结果为2。
*
* @param keys
* 要删除的key集合
* @return 删除了的key-value个数
* @date 2020/3/7 17:48:04
*/
public static long delete(Collection<String> keys) {
log.info("delete(...) => keys -> {}", keys);
Long count = redisTemplate.delete(keys);
log.info("delete(...) => count -> {}", count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
* 将key对应的value值进行序列化,并返回序列化后的value值。
*
* 注: 若不存在对应的key, 则返回null。
* 注: dump时,并不会删除redis中的对应key-value。
* 注: dump功能与restore相反。
*
* @param key
* 要序列化的value的key
* @return 序列化后的value值
* @date 2020/3/8 11:34:13
*/
public static byte[] dump(String key) {
log.info("dump(...) =>key -> {}", key);
byte[] result = redisTemplate.dump(key);
log.info("dump(...) => result -> {}", result);
return result;
}
/**
* 将给定的value值,反序列化到redis中, 形成新的key-value。
*
* @param key
* value对应的key
* @param value
* 要反序列的value值。
* 注: 这个值可以由{@link this#dump(String)}获得
* @param timeToLive
* 反序列化后的key-value的存活时长
* @param unit
* timeToLive的单位
*
* @throws RedisSystemException
* 如果redis中已存在同样的key时,抛出此异常
* @date 2020/3/8 11:36:45
*/
public static void restore(String key, byte[] value, long timeToLive, TimeUnit unit) {
restore(key, value, timeToLive, unit, false);
}
/**
* 将给定的value值,反序列化到redis中, 形成新的key-value。
*
* @param key
* value对应的key
* @param value
* 要反序列的value值。
* 注: 这个值可以由{@link this#dump(String)}获得
* @param timeout
* 反序列化后的key-value的存活时长
* @param unit
* timeout的单位
* @param replace
* 若redis中已经存在了相同的key, 是否替代原来的key-value
*
* @throws RedisSystemException
* 如果redis中已存在同样的key, 且replace为false时,抛出此异常
* @date 2020/3/8 11:36:45
*/
public static void restore(String key, byte[] value, long timeout, TimeUnit unit, boolean replace) {
log.info("restore(...) => key -> {}, value -> {}, timeout -> {}, unit -> {}, replace -> {}",
key, value, timeout, unit, replace);
redisTemplate.restore(key, value, timeout, unit, replace);
}
/**
* redis中是否存在,指定key的key-value
*
* @param key
* 指定的key
* @return 是否存在对应的key-value
* @date 2020/3/8 12:16:46
*/
public static boolean hasKey(String key) {
log.info("hasKey(...) => key -> {}", key);
Boolean result = redisTemplate.hasKey(key);
log.info("hasKey(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 给指定的key对应的key-value设置: 多久过时
*
* 注:过时后,redis会自动删除对应的key-value。
* 注:若key不存在,那么也会返回false。
*
* @param key
* 指定的key
* @param timeout
* 过时时间
* @param unit
* timeout的单位
* @return 操作是否成功
* @date 2020/3/8 12:18:58
*/
public static boolean expire(String key, long timeout, TimeUnit unit) {
log.info("expire(...) => key -> {}, timeout -> {}, unit -> {}", key, timeout, unit);
Boolean result = redisTemplate.expire(key, timeout, unit);
log.info("expire(...) => result is -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 给指定的key对应的key-value设置: 什么时候过时
*
* 注:过时后,redis会自动删除对应的key-value。
* 注:若key不存在,那么也会返回false。
*
* @param key
* 指定的key
* @param date
* 啥时候过时
*
* @return 操作是否成功
* @date 2020/3/8 12:19:29
*/
public static boolean expireAt(String key, Date date) {
log.info("expireAt(...) => key -> {}, date -> {}", key, date);
Boolean result = redisTemplate.expireAt(key, date);
log.info("expireAt(...) => result is -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 找到所有匹配pattern的key,并返回该key的结合.
*
* 提示:若redis中键值对较多,此方法耗时相对较长,慎用!慎用!慎用!
*
* @param pattern
* 匹配模板。
* 注: 常用的通配符有:
* ? 有且只有一个;
* * >=0哥;
*
* @return 匹配pattern的key的集合。 可能为null。
* @date 2020/3/8 12:38:38
*/
public static Set<String> keys(String pattern) {
log.info("keys(...) => pattern -> {}", pattern);
Set<String> keys = redisTemplate.keys(pattern);
log.info("keys(...) => keys -> {}", keys);
return keys;
}
/**
* 将当前数据库中的key对应的key-value,移动到对应位置的数据库中。
*
* 注:单机版的redis,默认将存储分为16个db, index为0 到 15。
* 注:同一个db下,key唯一; 但是在不同db中,key可以相同。
* 注:若目标db下,已存在相同的key, 那么move会失败,返回false。
*
* @param key
* 定位要移动的key-value的key
* @param dbIndex
* 要移动到哪个db
* @return 移动是否成功。
* 注: 若目标db下,已存在相同的key, 那么move会失败,返回false。
* @date 2020/3/8 13:01:00
*/
public static boolean move(String key, int dbIndex) {
log.info("move(...) => key -> {}, dbIndex -> {}", key, dbIndex);
Boolean result = redisTemplate.move(key, dbIndex);
log.info("move(...) =>result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 移除key对应的key-value的过期时间, 使该key-value一直存在
*
* 注: 若key对应的key-value,本身就是一直存在(无过期时间的), 那么persist方法会返回false;
* 若没有key对应的key-value存在,本那么persist方法会返回false;
*
* @param key
* 定位key-value的key
* @return 操作是否成功
* @date 2020/3/8 13:10:02
*/
public static boolean persist(String key) {
log.info("persist(...) => key -> {}", key);
Boolean result = redisTemplate.persist(key);
log.info("persist(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 获取key对应的key-value的过期时间
*
* 注: 若key-value永不过期, 那么返回的为-1。
* 注: 若不存在key对应的key-value, 那么返回的为-2
* 注:若存在零碎时间不足1 SECONDS,则(大体上)四舍五入到SECONDS级别。
*
* @param key
* 定位key-value的key
* @return 过期时间(单位s)
* @date 2020/3/8 13:17:35
*/
public static long getExpire(String key) {
Long result = getExpire(key, TimeUnit.SECONDS);
return result;
}
/**
* 获取key对应的key-value的过期时间
*
* 注: 若key-value永不过期, 那么返回的为-1。
* 注: 若不存在key对应的key-value, 那么返回的为-2
* 注:若存在零碎时间不足1 unit,则(大体上)四舍五入到unit别。
*
* @param key
* 定位key-value的key
* @return 过期时间(单位unit)
* @date 2020/3/8 13:17:35
*/
public static long getExpire(String key, TimeUnit unit) {
log.info("getExpire(...) =>key -> {}, unit is -> {}", key, unit);
Long result = redisTemplate.getExpire(key, unit);
log.info("getExpire(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 从redis的所有key中,随机获取一个key
*
* 注: 若redis中不存在任何key-value, 那么这里返回null
*
* @return 随机获取到的一个key
* @date 2020/3/8 14:11:43
*/
public static String randomKey() {
String result = redisTemplate.randomKey();
log.info("randomKey(...) => result is -> {}", result);
return result;
}
/**
* 重命名对应的oldKey为新的newKey
*
* 注: 若oldKey不存在, 则会抛出异常.
* 注: 若redis中已存在与newKey一样的key,
* 那么原key-value会被丢弃,
* 只留下新的key,以及原来的value
* 示例说明: 假设redis中已有 (keyAlpha, valueAlpha) 和 (keyBeta, valueBeat),
* 在使用rename(keyAlpha, keyBeta)替换后, redis中只会剩下(keyBeta, valueAlpha)
*
* @param oldKey
* 旧的key
* @param newKey
* 新的key
* @throws RedisSystemException
* 若oldKey不存在时, 抛出此异常
* @date 2020/3/8 14:14:17
*/
public static void rename(String oldKey, String newKey) {
log.info("rename(...) => oldKey -> {}, newKey -> {}", oldKey, newKey);
redisTemplate.rename(oldKey, newKey);
}
/**
* 当redis中不存在newKey时, 重命名对应的oldKey为新的newKey。
* 否者不进行重命名操作。
*
* 注: 若oldKey不存在, 则会抛出异常.
*
* @param oldKey
* 旧的key
* @param newKey
* 新的key
* @throws RedisSystemException
* 若oldKey不存在时, 抛出此异常
* @date 2020/3/8 14:14:17
*/
public static boolean renameIfAbsent(String oldKey, String newKey) {
log.info("renameIfAbsent(...) => oldKey -> {}, newKey -> {}", oldKey, newKey);
Boolean result = redisTemplate.renameIfAbsent(oldKey, newKey);
log.info("renameIfAbsent(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 获取key对应的value的数据类型
*
* 注: 若redis中不存在该key对应的key-value, 那么这里返回NONE。
*
* @param key
* 用于定位的key
* @return key对应的value的数据类型
* @date 2020/3/8 14:40:16
*/
public static DataType type(String key) {
log.info("type(...) => key -> {}", key);
DataType result = redisTemplate.type(key);
log.info("type(...) => result -> {}", result);
return result;
}
}
/**
* string相关操作
*
* 提示: redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例一).png
* redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例二).png
*
* @author JustryDeng
* @date 2020/3/7 16:54:25
*/
public static class StringOps {
/**
* 设置key-value
*
* 注: 若已存在相同的key, 那么原来的key-value会被丢弃。
*
* @param key
* key
* @param value
* key对应的value
* @date 2020/3/8 15:40:59
*/
public static void set(String key, String value) {
log.info("set(...) => key -> {}, value -> {}", key, value);
redisTemplate.opsForValue().set(key, value);
}
/**
* 处理redis中key对应的value值, 将第offset位的值, 设置为1或0。
*
* 说明: 在redis中,存储的字符串都是以二级制的进行存在的; 如存储的key-value里,值为abc,实际上,
* 在redis里面存储的是011000010110001001100011,前8为对应a,中间8为对应b,后面8位对应c。
* 示例:这里如果setBit(key, 6, true)的话,就是将索引位置6的那个数,设置值为1,值就变成
* 了011000110110001001100011
* 追注:offset即index,从0开始。
*
* 注: 参数value为true, 则设置为1;参数value为false, 则设置为0。
*
* 注: 若redis中不存在对应的key,那么会自动创建新的。
* 注: offset可以超过value在二进制下的索引长度。
*
* @param key
* 定位value的key
* @param offset
* 要改变的bit的索引
* @param value
* 改为1或0, true - 改为1, false - 改为0
*
* @return set是否成功
* @date 2020/3/8 16:30:37
*/
public static boolean setBit(String key, long offset, boolean value) {
log.info("setBit(...) => key -> {}, offset -> {}, value -> {}", key, offset, value);
Boolean result = redisTemplate.opsForValue().setBit(key, offset, value);
log.info("setBit(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 设置key-value
*
* 注: 若已存在相同的key, 那么原来的key-value会被丢弃
*
* @param key
* key
* @param value
* key对应的value
* @param timeout
* 过时时长
* @param unit
* timeout的单位
* @date 2020/3/8 15:40:59
*/
public static void setEx(String key, String value, long timeout, TimeUnit unit) {
log.info("setEx(...) => key -> {}, value -> {}, timeout -> {}, unit -> {}",
key, value, timeout, unit);
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
/**
* 若不存在key时, 向redis中添加key-value, 返回成功/失败。
* 若存在,则不作任何操作, 返回false。
*
* @param key
* key
* @param value
* key对应的value
*
* @return set是否成功
* @date 2020/3/8 16:51:36
*/
public static boolean setIfAbsent(String key, String value) {
log.info("setIfAbsent(...) => key -> {}, value -> {}", key, value);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value);
log.info("setIfAbsent(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 若不存在key时, 向redis中添加一个(具有超时时长的)key-value, 返回成功/失败。
* 若存在,则不作任何操作, 返回false。
*
* @param key
* key
* @param value
* key对应的value
* @param timeout
* 超时时长
* @param unit
* timeout的单位
*
* @return set是否成功
* @date 2020/3/8 16:51:36
*/
public static boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
log.info("setIfAbsent(...) => key -> {}, value -> {}, key -> {}, value -> {}", key, value, timeout, unit);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
log.info("setIfAbsent(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 从(redis中key对应的)value的offset位置起(包含该位置),用replaceValue替换对应长度的值。
*
* 举例说明:
* 1.假设redis中存在key-value ("ds", "0123456789"); 调
* 用setRange("ds", "abcdefghijk", 3)后, redis中该value值就变为了[012abcdefghijk]
*
* 2.假设redis中存在key-value ("jd", "0123456789");调
* 用setRange("jd", "xyz", 3)后, redis中该value值就变为了[012xyz6789]
*
* 3.假设redis中存在key-value ("ey", "0123456789");调
* 用setRange("ey", "qwer", 15)后, redis中该value值就变为了[0123456789 qwer]
* 注:case3比较特殊,offset超过了原value的长度了, 中间就会有一些空格来填充,但是如果在程序
* 中直接输出的话,中间那部分空格可能会出现乱码。
*
* @param key
* 定位key-value的key
* @param replaceValue
* 要替换的值
* @param offset
* 起始位置
* @date 2020/3/8 17:04:31
*/
public static void setRange(String key, String replaceValue, long offset) {
log.info("setRange(...) => key -> {}, replaceValue -> {}, offset -> {}", key, replaceValue, offset);
redisTemplate.opsForValue().set(key, replaceValue, offset);
}
/**
* 获取到key对应的value的长度。
*
* 注: 长度等于{@link String#length}。
* 注: 若redis中不存在对应的key-value, 则返回值为0.
*
* @param key
* 定位value的key
* @return value的长度
* @date 2020/3/8 17:14:30
*/
public static long size(String key) {
log.info("size(...) => key -> {}", key);
Long result = redisTemplate.opsForValue().size(key);
log.info("size(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 批量设置 key-value
*
* 注: 若存在相同的key, 则原来的key-value会被丢弃。
*
* @param maps
* key-value 集
* @date 2020/3/8 17:21:19
*/
public static void multiSet(Map<String, String> maps) {
log.info("multiSet(...) => maps -> {}", maps);
redisTemplate.opsForValue().multiSet(maps);
}
/**
* 当redis中,不存在任何一个keys时, 才批量设置 key-value, 并返回成功/失败.
* 否者,不进行任何操作, 并返回false。
*
* 即: 假设调用此方法时传入的参数map是这样的: {k1=v1, k2=v2, k3=v3}
* 那么redis中, k1、k2、k3都不存在时,才会批量设置key-value;
* 否则不会设置任何key-value。
*
* 注: 若存在相同的key, 则原来的key-value会被丢弃。
*
* 注:
*
* @param maps
* key-value 集
*
* @return 操作是否成功
* @date 2020/3/8 17:21:19
*/
public static boolean multiSetIfAbsent(Map<String, String> maps) {
log.info("multiSetIfAbsent(...) => maps -> {}", maps);
Boolean result = redisTemplate.opsForValue().multiSetIfAbsent(maps);
log.info("multiSetIfAbsent(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 增/减 整数
*
* 注: 负数则为减。
* 注: 若key对应的value值不支持增/减操作(即: value不是数字), 那么会
* 抛出org.springframework.data.redis.RedisSystemException
*
* @param key
* 用于定位value的key
* @param increment
* 增加多少
* @return 增加后的总值。
* @throws RedisSystemException key对应的value值不支持增/减操作时
* @date 2020/3/8 17:45:51
*/
public static long incrBy(String key, long increment) {
log.info("incrBy(...) => key -> {}, increment -> {}", key, increment);
Long result = redisTemplate.opsForValue().increment(key, increment);
log.info("incrBy(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 增/减 浮点数
*
* 注: 慎用浮点数,会有精度问题。
* 如: 先 RedisUtil.StringOps.set("ds", "123");
* 然后再RedisUtil.StringOps.incrByFloat("ds", 100.6);
* 就会看到精度问题。
* 注: 负数则为减。
* 注: 若key对应的value值不支持增/减操作(即: value不是数字), 那么会
* 抛出org.springframework.data.redis.RedisSystemException
*
* @param key
* 用于定位value的key
* @param increment
* 增加多少
* @return 增加后的总值。
* @throws RedisSystemException key对应的value值不支持增/减操作时
* @date 2020/3/8 17:45:51
*/
public static double incrByFloat(String key, double increment) {
log.info("incrByFloat(...) => key -> {}, increment -> {}", key, increment);
Double result = redisTemplate.opsForValue().increment(key, increment);
log.info("incrByFloat(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 追加值到末尾
*
* 注: 当redis中原本不存在key时,那么(从效果上来看)此方法就等价于{@link this#set(String, String)}
*
* @param key
* 定位value的key
* @param value
* 要追加的value值
* @return 追加后, 整个value的长度
* @date 2020/3/8 17:59:21
*/
public static int append(String key, String value) {
log.info("append(...) => key -> {}, value -> {}", key, value);
Integer result = redisTemplate.opsForValue().append(key, value);
log.info("append(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 根据key,获取到对应的value值
*
* @param key
* key-value对应的key
* @return 该key对应的值。
* 注: 若key不存在, 则返回null。
*
* @date 2020/3/8 16:27:41
*/
public static String get(String key) {
log.info("get(...) => key -> {}", key);
String result = redisTemplate.opsForValue().get(key);
log.info("get(...) => result -> {} ", result);
return result;
}
/**
* 对(key对应的)value进行截取, 截取范围为[start, end]
*
* 注: 若[start, end]的范围不在value的范围中,那么返回的是空字符串 ""
* 注: 若value只有一部分在[start, end]的范围中,那么返回的是value对应部分的内容(即:不足的地方,并不会以空来填充)
*
* @param key
* 定位value的key
* @param start
* 起始位置 (从0开始)
* @param end
* 结尾位置 (从0开始)
* @return 截取后的字符串
* @date 2020/3/8 18:08:45
*/
public static String getRange(String key, long start, long end) {
log.info("getRange(...) => kry -> {}", key);
String result = redisTemplate.opsForValue().get(key, start, end);
log.info("getRange(...) => result -> {} ", result);
return result;
}
/**
* 给指定key设置新的value, 并返回旧的value
*
* 注: 若redis中不存在key, 那么此操作仍然可以成功, 不过返回的旧值是null
*
* @param key
* 定位value的key
* @param newValue
* 要为该key设置的新的value值
* @return 旧的value值
* @date 2020/3/8 18:14:24
*/
public static String getAndSet(String key, String newValue) {
log.info("getAndSet(...) => key -> {}, value -> {}", key, newValue);
String oldValue = redisTemplate.opsForValue().getAndSet(key, newValue);
log.info("getAndSet(...) => oldValue -> {}", oldValue);
return oldValue;
}
/**
* 获取(key对应的)value在二进制下,offset位置的bit值。
*
* 注: 当offset的值在(二进制下的value的)索引范围外时, 返回的也是false。
*
* 示例:
* RedisUtil.StringOps.set("akey", "a");
* 字符串a, 转换为二进制为01100001
* 那么getBit("akey", 6)获取到的结果为false。
*
* @param key
* 定位value的key
* @param offset
* 定位bit的索引
* @return offset位置对应的bit的值(true - 1, false - 0)
* @date 2020/3/8 18:21:10
*/
public static boolean getBit(String key, long offset) {
log.info("getBit(...) => key -> {}, offset -> {}", key, offset);
Boolean result = redisTemplate.opsForValue().getBit(key, offset);
log.info("getBit(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 批量获取value值
*
* 注: 若redis中,对应的key不存在,那么该key对应的返回的value值为null
*
* @param keys
* key集
* @return value值集合
* @date 2020/3/8 18:26:33
*/
public static List<String> multiGet(Collection<String> keys) {
log.info("multiGet(...) => keys -> {}", keys);
List<String> result = redisTemplate.opsForValue().multiGet(keys);
log.info("multiGet(...) => result -> {}", result);
return result;
}
}
/**
* hash相关操作
*
* 提示: 简单的,可以将redis中hash的数据结构看作是 Map<String, Map<HK, HV>>
* 提示: redis中String的数据结构可参考resources/data-structure/Hash(散列)的数据结构(示例一).png
* redis中String的数据结构可参考resources/data-structure/Hash(散列)的数据结构(示例二).png
*
* @author JustryDeng
* @date 2020/3/8 23:39:26
*/
public static class HashOps {
/**
* 向key对应的hash中,增加一个键值对entryKey-entryValue
*
* 注: 同一个hash里面,若已存在相同的entryKey, 那么此操作将丢弃原来的entryKey-entryValue,
* 而使用新的entryKey-entryValue。
*
*
* @param key
* 定位hash的key
* @param entryKey
* 要向hash中增加的键值对里的 键
* @param entryValue
* 要向hash中增加的键值对里的 值
* @date 2020/3/8 23:49:52
*/
public static void hPut(String key, String entryKey, String entryValue) {
log.info("hPut(...) => key -> {}, entryKey -> {}, entryValue -> {}", key, entryKey, entryValue);
redisTemplate.opsForHash().put(key, entryKey, entryValue);
}
/**
* 向key对应的hash中,增加maps(即: 批量增加entry集)
*
* 注: 同一个hash里面,若已存在相同的entryKey, 那么此操作将丢弃原来的entryKey-entryValue,
* 而使用新的entryKey-entryValue
*
* @param key
* 定位hash的key
* @param maps
* 要向hash中增加的键值对集
* @date 2020/3/8 23:49:52
*/
public static void hPutAll(String key, Map<String, String> maps) {
log.info("hPutAll(...) => key -> {}, maps -> {}", key, maps);
redisTemplate.opsForHash().putAll(key, maps);
}
/**
* 当key对应的hash中,不存在entryKey时,才(向key对应的hash中,)增加entryKey-entryValue
* 否者,不进行任何操作
*
* @param key
* 定位hash的key
* @param entryKey
* 要向hash中增加的键值对里的 键
* @param entryValue
* 要向hash中增加的键值对里的 值
*
* @return 操作是否成功。
* @date 2020/3/8 23:49:52
*/
public static boolean hPutIfAbsent(String key, String entryKey, String entryValue) {
log.info("hPutIfAbsent(...) => key -> {}, entryKey -> {}, entryValue -> {}",
key, entryKey, entryValue);
Boolean result = redisTemplate.opsForHash().putIfAbsent(key, entryKey, entryValue);
log.info("hPutIfAbsent(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 获取到key对应的hash里面的对应字段的值
*
* 注: 若redis中不存在对应的key, 则返回null。
* 若key对应的hash中不存在对应的entryKey, 也会返回null。
*
* @param key
* 定位hash的key
* @param entryKey
* 定位hash里面的entryValue的entryKey
*
* @return key对应的hash里的entryKey对应的entryValue值
* @date 2020/3/9 9:09:30
*/
public static Object hGet(String key, String entryKey) {
log.info("hGet(...) => key -> {}, entryKey -> {}", key, entryKey);
Object entryValue = redisTemplate.opsForHash().get(key, entryKey);
log.info("hGet(...) => entryValue -> {}", entryValue);
return entryValue;
}
/**
* 获取到key对应的hash(即: 获取到key对应的Map<HK, HV>)
*
* 注: 若redis中不存在对应的key, 则返回一个没有任何entry的空的Map(,而不是返回null)。
*
* @param key
* 定位hash的key
*
* @return key对应的hash。
* @date 2020/3/9 9:09:30
*/
public static Map<Object, Object> hGetAll(String key) {
log.info("hGetAll(...) => key -> {}", key);
Map<Object, Object> result = redisTemplate.opsForHash().entries(key);
log.info("hGetAll(...) => result -> {}", result);
return result;
}
/**
* 批量获取(key对应的)hash中的entryKey的entryValue
*
* 注: 若hash中对应的entryKey不存在,那么返回的对应的entryValue值为null
* 注: redis中key不存在,那么返回的List中,每个元素都为null。
* 追注: 这个List本身不为null, size也不为0, 只是每个list中的每个元素为null而已。
*
* @param key
* 定位hash的key
* @param entryKeys
* 需要获取的hash中的字段集
* @return hash中对应entryKeys的对应entryValue集
* @date 2020/3/9 9:25:38
*/
public static List<Object> hMultiGet(String key, Collection<Object> entryKeys) {
log.info("hMultiGet(...) => key -> {}, entryKeys -> {}", key, entryKeys);
List<Object> entryValues = redisTemplate.opsForHash().multiGet(key, entryKeys);
log.info("hMultiGet(...) => entryValues -> {}", entryValues);
return entryValues;
}
/**
* (批量)删除(key对应的)hash中的对应entryKey-entryValue
*
* 注: 1、若redis中不存在对应的key, 则返回0;
* 2、若要删除的entryKey,在key对应的hash中不存在,在count不会+1, 如:
* RedisUtil.HashOps.hPut("ds", "name", "邓沙利文");
* RedisUtil.HashOps.hPut("ds", "birthday", "1994-02-05");
* RedisUtil.HashOps.hPut("ds", "hobby", "女");
* 则调用RedisUtil.HashOps.hDelete("ds", "name", "birthday", "hobby", "non-exist-entryKey")
* 的返回结果为3
* 注: 若(key对应的)hash中的所有entry都被删除了,那么该key也会被删除
*
* @param key
* 定位hash的key
* @param entryKeys
* 定位要删除的entryKey-entryValue的entryKey
*
* @return 删除了对应hash中多少个entry
* @date 2020/3/9 9:37:47
*/
public static long hDelete(String key, Object... entryKeys) {
log.info("hDelete(...) => key -> {}, entryKeys -> {}", key, entryKeys);
Long count = redisTemplate.opsForHash().delete(key, entryKeys);
log.info("hDelete(...) => count -> {}", count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
* 查看(key对应的)hash中,是否存在entryKey对应的entry
*
* 注: 若redis中不存在key,则返回false。
* 注: 若key对应的hash中不存在对应的entryKey, 也会返回false。
*
* @param key
* 定位hash的key
* @param entryKey
* 定位hash中entry的entryKey
*
* @return hash中是否存在entryKey对应的entry.
* @date 2020/3/9 9:51:55
*/
public static boolean hExists(String key, String entryKey) {
log.info("hDelete(...) => key -> {}, entryKeys -> {}", key, entryKey);
Boolean exist = redisTemplate.opsForHash().hasKey(key, entryKey);
log.info("hDelete(...) => exist -> {}", exist);
return exist;
}
/**
* 增/减(hash中的某个entryValue值) 整数
*
* 注: 负数则为减。
* 注: 若key不存在,那么会自动创建对应的hash,并创建对应的entryKey、entryValue,entryValue的初始值为increment。
* 注: 若entryKey不存在,那么会自动创建对应的entryValue,entryValue的初始值为increment。
* 注: 若key对应的value值不支持增/减操作(即: value不是数字), 那么会
* 抛出org.springframework.data.redis.RedisSystemException
*
* @param key
* 用于定位hash的key
* @param entryKey
* 用于定位entryValue的entryKey
* @param increment
* 增加多少
* @return 增加后的总值。
* @throws RedisSystemException key对应的value值不支持增/减操作时
* @date 2020/3/9 10:09:28
*/
public static long hIncrBy(String key, Object entryKey, long increment) {
log.info("hIncrBy(...) => key -> {}, entryKey -> {}, increment -> {}",
key, entryKey, increment);
Long result = redisTemplate.opsForHash().increment(key, entryKey, increment);
log.info("hIncrBy(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 增/减(hash中的某个entryValue值) 浮点数
*
* 注: 负数则为减。
* 注: 若key不存在,那么会自动创建对应的hash,并创建对应的entryKey、entryValue,entryValue的初始值为increment。
* 注: 若entryKey不存在,那么会自动创建对应的entryValue,entryValue的初始值为increment。
* 注: 若key对应的value值不支持增/减操作(即: value不是数字), 那么会
* 抛出org.springframework.data.redis.RedisSystemException
* 注: 因为是浮点数, 所以可能会和{@link StringOps#incrByFloat(String, double)}一样, 出现精度问题。
* 追注: 本人简单测试了几组数据,暂未出现精度问题。
*
* @param key
* 用于定位hash的key
* @param entryKey
* 用于定位entryValue的entryKey
* @param increment
* 增加多少
* @return 增加后的总值。
* @throws RedisSystemException key对应的value值不支持增/减操作时
* @date 2020/3/9 10:09:28
*/
public static double hIncrByFloat(String key, Object entryKey, double increment) {
log.info("hIncrByFloat(...) => key -> {}, entryKey -> {}, increment -> {}",
key, entryKey, increment);
Double result = redisTemplate.opsForHash().increment(key, entryKey, increment);
log.info("hIncrByFloat(...) => result -> {}", result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
* 获取(key对应的)hash中的所有entryKey
*
* 注: 若key不存在,则返回的是一个空的Set(,而不是返回null)
*
* @param key
* 定位hash的key
*
* @return hash中的所有entryKey
* @date 2020/3/9 10:30:13
*/
public static Set<Object> hKeys(String key) {
log.info("hKeys(...) => key -> {}", key);
Set<Object> entryKeys = redisTemplate.opsForHash().keys(key);
log.info("hKeys(...) => entryKeys -> {}", entryKeys);
return entryKeys;
}
/**
* 获取(key对应的)hash中的所有entryValue
*
* 注: 若key不存在,则返回的是一个空的List(,而不是返回null)
*
* @param key
* 定位hash的key
*
* @return hash中的所有entryValue
* @date 2020/3/9 10:30:13
*/
public static List<Object> hValues(String key) {
log.info("hValues(...) => key -> {}", key);
List<Object> entryValues = redisTemplate.opsForHash().values(key);
log.info("hValues(...) => entryValues -> {}", entryValues);
return entryValues;
}
/**
* 获取(key对应的)hash中的所有entry的数量
*
* 注: 若redis中不存在对应的key, 则返回值为0
*
* @param key
* 定位hash的key
*
* @return (key对应的)hash中,entry的个数
* @date 2020/3/9 10:41:01
*/
public static long hSize(String key) {
log.info("hSize(...) => key -> {}", key);
Long count = redisTemplate.opsForHash().size(key);
log.info("hSize(...) => count -> {}", count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
* 根据options匹配到(key对应的)hash中的对应的entryKey, 并返回对应的entry集
*
*
* 注: ScanOptions实例的创建方式举例:
* 1、ScanOptions.NONE
* 2、ScanOptions.scanOptions().match("n??e").build()
*
* @param key
* 定位hash的key
* @param options
* 匹配entryKey的条件
* 注: ScanOptions.NONE表示全部匹配。
* 注: ScanOptions.scanOptions().match(pattern).build()表示按照pattern匹配,
* 其中pattern中可以使用通配符 * ? 等,
* * 表示>=0个字符
* ? 表示有且只有一个字符
* 此处的匹配规则与{@link KeyOps#keys(String)}处的一样。
*
* @return 匹配到的(key对应的)hash中的entry
* @date 2020/3/9 10:49:27
*/
public static Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
log.info("hScan(...) => key -> {}, options -> {}", key, JSON.toJSONString(options));
Cursor<Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options);
log.info("hScan(...) => cursor -> {}", JSON.toJSONString(cursor));
return cursor;
}
}
/**
* list相关操作
*
* 提示: 列表中的元素,可以重复。
*
* 提示: list是有序的。
*
* 提示: redis中的list中的索引,可分为两类,这两类都可以用来定位list中元素:
* 类别一: 从left到right, 是从0开始依次增大: 0, 1, 2, 3...
* 类别二: 从right到left, 是从-1开始依次减小: -1, -2, -3, -4...
*
* 提示: redis中String的数据结构可参考resources/data-structure/List(列表)的数据结构(示例一).png
* redis中String的数据结构可参考resources/data-structure/List(列表)的数据结构(示例二).png
*
* @author JustryDeng
* @date 2020/3/9 11:30:48
*/
public static class ListOps {
/**
* 从左端推入元素进列表
*
* 注: 若redis中不存在对应的key, 那么会自动创建
*
* @param key
* 定位list的key
* @param item
* 要推入list的元素
*
* @return 推入后,(key对应的)list的size
* @date 2020/3/9 11:56:05
*/
public static long lLeftPush(String key, String item) {
log.info("lLeftPush(...) => key -> {}, item -> {}", key, item);
Long size = redisTemplate.opsForList().leftPush(key, item);
log.info("lLeftPush(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 从左端批量推入元素进列表
*
* 注: 若redis中不存在对应的key, 那么会自动创建
* 注: 这一批item中,先push左侧的, 后push右侧的
*
* @param key
* 定位list的key
* @param items
* 要批量推入list的元素集
*
* @return 推入后,(key对应的)list的size
* @date 2020/3/9 11:56:05
*/
public static long lLeftPushAll(String key, String... items) {
log.info("lLeftPushAll(...) => key -> {}, items -> {}", key, items);
Long size = redisTemplate.opsForList().leftPushAll(key, items);
log.info("lLeftPushAll(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 从左端批量推入元素进列表
*
* 注: 若redis中不存在对应的key, 那么会自动创建
* 注: 这一批item中,那个item先从Collection取出来,就先push哪个
*
* @param key
* 定位list的key
* @param items
* 要批量推入list的元素集
*
* @return 推入后,(key对应的)list的size
* @date 2020/3/9 11:56:05
*/
public static long lLeftPushAll(String key, Collection<String> items) {
log.info("lLeftPushAll(...) => key -> {}, items -> {}", key, items);
Long size = redisTemplate.opsForList().leftPushAll(key, items);
log.info("lLeftPushAll(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 如果redis中存在key, 则从左端批量推入元素进列表;
* 否则,不进行任何操作
*
* @param key
* 定位list的key
* @param item
* 要推入list的项
*
* @return 推入后,(key对应的)list的size
* @date 2020/3/9 13:40:08
*/
public static long lLeftPushIfPresent(String key, String item) {
log.info("lLeftPushIfPresent(...) => key -> {}, item -> {}", key, item);
Long size = redisTemplate.opsForList().leftPushIfPresent(key, item);
log.info("lLeftPushIfPresent(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 若key对应的list中存在pivot项, 那么将item放入第一个pivot项前(即:放在第一个pivot项左边);
* 若key对应的list中不存在pivot项, 那么不做任何操作, 直接返回-1。
*
* 注: 若redis中不存在对应的key, 那么会自动创建
*
* @param key
* 定位list的key
* @param item
* 要推入list的元素
*
* @return 推入后,(key对应的)list的size
* @date 2020/3/9 11:56:05
*/
public static long lLeftPush(String key, String pivot, String item) {
log.info("lLeftPush(...) => key -> {}, pivot -> {}, item -> {}", key, pivot, item);
Long size = redisTemplate.opsForList().leftPush(key, pivot, item);
log.info("lLeftPush(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 与{@link ListOps#lLeftPush(String, String)}类比即可, 不过是从list右侧推入元素
*/
public static long lRightPush(String key, String item) {
log.info("lRightPush(...) => key -> {}, item -> {}", key, item);
Long size = redisTemplate.opsForList().rightPush(key, item);
log.info("lRightPush(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 与{@link ListOps#lLeftPushAll(String, String...)}类比即可, 不过是从list右侧推入元素
*/
public static long lRightPushAll(String key, String... items) {
log.info("lRightPushAll(...) => key -> {}, items -> {}", key, items);
Long size = redisTemplate.opsForList().rightPushAll(key, items);
log.info("lRightPushAll(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 与{@link ListOps#lLeftPushAll(String, Collection<String>)}类比即可, 不过是从list右侧推入元素
*/
public static long lRightPushAll(String key, Collection<String> items) {
log.info("lRightPushAll(...) => key -> {}, items -> {}", key, items);
Long size = redisTemplate.opsForList().rightPushAll(key, items);
log.info("lRightPushAll(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 与{@link ListOps#lLeftPushIfPresent(String, String)}类比即可, 不过是从list右侧推入元素
*/
public static long lRightPushIfPresent(String key, String item) {
log.info("lRightPushIfPresent(...) => key -> {}, item -> {}", key, item);
Long size = redisTemplate.opsForList().rightPushIfPresent(key, item);
log.info("lRightPushIfPresent(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 与{@link ListOps#lLeftPush(String, String, String)}类比即可, 不过是从list右侧推入元素
*/
public static long lRightPush(String key, String pivot, String item) {
log.info("lLeftPush(...) => key -> {}, pivot -> {}, item -> {}", key, pivot, item);
Long size = redisTemplate.opsForList().rightPush(key, pivot, item);
log.info("lLeftPush(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 【非阻塞队列】 从左侧移出(key对应的)list中的第一个元素, 并将该元素返回
*
* 注: 此方法是非阻塞的, 即: 若(key对应的)list中的所有元素都被pop移出了,此时,再进行pop的话,会立即返回null
* 注: 此方法是非阻塞的, 即: 若redis中不存在对应的key,那么会立即返回null
* 注: 若将(key对应的)list中的所有元素都pop完了,那么该key会被删除
*
* @param key
* 定位list的key
* @return 移出的那个元素
* @date 2020/3/9 14:33:56
*/
public static String lLeftPop(String key) {
log.info("lLeftPop(...) => key -> {}", key);
String item = redisTemplate.opsForList().leftPop(key);
log.info("lLeftPop(...) => item -> {}", item);
return item;
}
/**
* 【阻塞队列】 从左侧移出(key对应的)list中的第一个元素, 并将该元素返回
*
* 注: 此方法是阻塞的, 即: 若(key对应的)list中的所有元素都被pop移出了,此时,再进行pop的话,
* 会阻塞timeout这么久,然后返回null
* 注: 此方法是阻塞的, 即: 若redis中不存在对应的key,那么会阻塞timeout这么久,然后返回null
* 注: 若将(key对应的)list中的所有元素都pop完了,那么该key会被删除
*
* 提示: 若阻塞过程中, 目标key-list出现了,且里面有item了,那么会立马停止阻塞, 进行元素移出并返回
*
* @param key
* 定位list的key
* @param timeout
* 超时时间
* @param unit
* timeout的单位
* @return 移出的那个元素
* @date 2020/3/9 14:33:56
*/
public static String lLeftPop(String key, long timeout, TimeUnit unit) {
log.info("lLeftPop(...) => key -> {}, timeout -> {}, unit -> {}", key, timeout, unit);
String item = redisTemplate.opsForList().leftPop(key, timeout, unit);
log.info("lLeftPop(...) => item -> {}", item);
return item;
}
/**
* 与{@link ListOps#lLeftPop(String)}类比即可, 不过是从list右侧移出元素
*/
public static String lRightPop(String key) {
log.info("lRightPop(...) => key -> {}", key);
String item = redisTemplate.opsForList().rightPop(key);
log.info("lRightPop(...) => item -> {}", item);
return item;
}
/**
* 与{@link ListOps#lLeftPop(String, long, TimeUnit)}类比即可, 不过是从list右侧移出元素
*/
public static String lRightPop(String key, long timeout, TimeUnit unit) {
log.info("lRightPop(...) => key -> {}, timeout -> {}, unit -> {}", key, timeout, unit);
String item = redisTemplate.opsForList().rightPop(key, timeout, unit);
log.info("lRightPop(...) => item -> {}", item);
return item;
}
/**
* 【非阻塞队列】 从sourceKey对应的sourceList右侧移出一个item, 并将这个item推
* 入(destinationKey对应的)destinationList的左侧
*
* 注: 若sourceKey对应的list中没有item了,则立马认为(从sourceKey对应的list中pop出来的)item为null,
* null并不会往destinationKey对应的list中push。
* 追注: 此时,此方法的返回值是null。
*
* 注: 若将(sourceKey对应的)list中的所有元素都pop完了,那么该sourceKey会被删除。
*
* @param sourceKey
* 定位sourceList的key
* @param destinationKey
* 定位destinationList的key
*
* @return 移动的这个元素
* @date 2020/3/9 15:06:59
*/
public static String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
log.info("lRightPopAndLeftPush(...) => sourceKey -> {}, destinationKey -> {}",
sourceKey, destinationKey);
String item = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
log.info("lRightPopAndLeftPush(...) => item -> {}", item);
return item;
}
/**
* 【阻塞队列】 从sourceKey对应的sourceList右侧移出一个item, 并将这个item推
* 入(destinationKey对应的)destinationList的左侧
*
* 注: 若sourceKey对应的list中没有item了,则阻塞等待, 直到能从sourceList中移出一个非null的item(或等待时长超时);
* case1: 等到了一个非null的item, 那么继续下面的push操作,并返回这个item。
* case2: 超时了,还没等到非null的item, 那么pop出的结果就未null,此时并不会往destinationList进行push。
* 此时,此方法的返回值是null。
*
* 注: 若将(sourceKey对应的)list中的所有元素都pop完了,那么该sourceKey会被删除。
*
* @param sourceKey
* 定位sourceList的key
* @param destinationKey
* 定位destinationList的key
* @param timeout
* 超时时间
* @param unit
* timeout的单位
*
* @return 移动的这个元素
* @date 2020/3/9 15:06:59
*/
public static String lRightPopAndLeftPush(String sourceKey, String destinationKey, long timeout,
TimeUnit unit) {
log.info("lRightPopAndLeftPush(...) => sourceKey -> {}, destinationKey -> {}, timeout -> {},"
+ " unit -> {}", sourceKey, destinationKey, timeout, unit);
String item = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit);
log.info("lRightPopAndLeftPush(...) => item -> {}", item);
return item;
}
/**
* 设置(key对应的)list中对应索引位置index处的元素为item
*
* 注: 若key不存在,则会抛出org.springframework.data.redis.RedisSystemException
* 注: 若索引越界,也会抛出org.springframework.data.redis.RedisSystemException
*
* @param key
* 定位list的key
* @param index
* 定位list中的元素的索引
* @param item
* 要替换成的值
* @date 2020/3/9 15:39:50
*/
public static void lSet(String key, long index, String item) {
log.info("lSet(...) => key -> {}, index -> {}, item -> {}", key, index, item);
redisTemplate.opsForList().set(key, index, item);
}
/**
* 通过索引index, 获取(key对应的)list中的元素
*
* 注: 若key不存在 或 index超出(key对应的)list的索引范围,那么返回null
*
* @param key
* 定位list的key
* @param index
* 定位list中的item的索引
*
* @return list中索引index对应的item
* @date 2020/3/10 0:27:23
*/
public static String lIndex(String key, long index) {
log.info("lIndex(...) => key -> {}, index -> {}", key, index);
String item = redisTemplate.opsForList().index(key, index);
log.info("lIndex(...) => item -> {}", item);
return item;
}
/**
* 获取(key对应的)list中索引在[start, end]之间的item集
*
* 注: 含start、含end。
* 注: 当key不存在时,获取到的是空的集合。
* 注: 当获取的范围比list的范围还要大时,获取到的是这两个范围的交集。
*
* 提示: 可通过RedisUtil.ListOps.lRange(key, 0, -1)来获取到该key对应的整个list
*
* @param key
* 定位list的key
* @param start
* 起始元素的index
* @param end
* 结尾元素的index
*
* @return 对应的元素集合
* @date 2020/3/10 0:34:59
*/
public static List<String> lRange(String key, long start, long end) {
log.info("lRange(...) => key -> {}, start -> {}, end -> {}", key, start, end);
List<String> result = redisTemplate.opsForList().range(key, start, end);
log.info("lRange(...) => result -> {}", result);
return result;
}
/**
* 获取(key对应的)list
*
* @see ListOps#lRange(String, long, long)
*
* @param key
* 定位list的key
* @return (key对应的)list
* @date 2020/3/10 0:46:50
*/
public static List<String> lWholeList(String key) {
log.info("lWholeList(...) => key -> {}", key);
List<String> result = redisTemplate.opsForList().range(key, 0, -1);
log.info("lWholeList(...) => result -> {}", result);
return result;
}
/**
* 获取(key对应的)list的size
*
* 注: 当key不存在时,获取到的size为0.
*
* @param key
* 定位list的key
*
* @return list的size。
*
* @date 2020/3/10 0:48:40
*/
public static long lSize(String key) {
log.info("lSize(...) => key -> {}", key);
Long size = redisTemplate.opsForList().size(key);
log.info("lSize(...) => size -> {}", size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
* 删除(key对应的)list中,前expectCount个值等于item的项
*
* 注: 若expectCount == 0, 则表示删除list中所有的值等于item的项.
* 注: 若expectCount > 0, 则表示删除从左往右进行
* 注: 若expectCount < 0, 则表示删除从右往左进行
*
* 注: 若list中,值等于item的项的个数少于expectCount时,那么会删除list中所有的值等于item的项。
* 注: 当key不存在时, 返回0。
* 注: 若lRemove后, 将(key对应的)list中没有任何元素了,那么该key会被删除。
*
* @param key
* 定位list的key
* @param expectCount
* 要删除的item的个数
* @param item
* 要删除的item
*
* @return 实际删除了的item的个数
* @date 2020/3/10 0:52:57
*/
public static long lRemove(String key, long expectCount, String item) {
log.info("lRemove(...) => key -> {}, ex以上是关于Redis - 一篇走心的 RedisUtil 工具类的主要内容,如果未能解决你的问题,请参考以下文章