294 lines
5.7 KiB
Java
294 lines
5.7 KiB
Java
package com.imyeyu.spring.util;
|
|
|
|
import com.imyeyu.java.TimiJava;
|
|
import com.imyeyu.java.bean.timi.TimiException;
|
|
import com.imyeyu.spring.config.AbstractRedisConfig;
|
|
import lombok.AllArgsConstructor;
|
|
import lombok.Data;
|
|
import lombok.NoArgsConstructor;
|
|
import org.springframework.data.redis.connection.RedisConnection;
|
|
import org.springframework.data.redis.core.Cursor;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.data.redis.core.ScanOptions;
|
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
|
|
|
import java.time.Duration;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.function.Consumer;
|
|
|
|
/**
|
|
* RedisTemplate 功能封装,简化 Redis 操作
|
|
* <p>serializer 为该 RedisTemplate 的键的序列化操作,序列化解析器由 {@link AbstractRedisConfig} 提供
|
|
*
|
|
* @param <K> 键类型
|
|
* @param <V> 值类型
|
|
* @author 夜雨
|
|
* @version 2021-11-21 09:58
|
|
*/
|
|
@Data
|
|
@NoArgsConstructor
|
|
@AllArgsConstructor
|
|
public class Redis<K, V> {
|
|
|
|
private RedisTemplate<K, V> redis;
|
|
private RedisSerializer<K> serializer;
|
|
|
|
/**
|
|
* 加锁
|
|
*
|
|
* @param key 键
|
|
* @param value 值
|
|
* @param timeoutMS 超时时间毫秒
|
|
* @return true 为加锁成功
|
|
*/
|
|
public boolean lock(K key, V value, long timeoutMS) {
|
|
Boolean lock = redis.opsForValue().setIfAbsent(key, value, timeoutMS, TimeUnit.MILLISECONDS);
|
|
return lock != null && lock;
|
|
}
|
|
|
|
/**
|
|
* 释放锁
|
|
*
|
|
* @param key 键
|
|
*/
|
|
public void releaseLock(K key) {
|
|
destroy(key);
|
|
}
|
|
|
|
/**
|
|
* 设置存活时间
|
|
*
|
|
* @param key 键
|
|
* @param ms 毫秒 TTL
|
|
*/
|
|
public void setExpire(K key, long ms) {
|
|
redis.expire(key, Duration.ofMillis(ms));
|
|
}
|
|
|
|
/**
|
|
* 获取该数据 TTL
|
|
*
|
|
* @param key 键
|
|
* @return 毫秒 TTL
|
|
*/
|
|
public long getExpire(K key) {
|
|
return Objects.requireNonNullElse(redis.getExpire(key, TimeUnit.MILLISECONDS), -1L);
|
|
}
|
|
|
|
/**
|
|
* 设置数据并保持 TTL
|
|
*
|
|
* @param key 键
|
|
* @param value 值
|
|
*/
|
|
public void setAndKeepTTL(K key, V value) {
|
|
long expire = redis.getExpire(key, TimeUnit.MILLISECONDS);
|
|
if (expire <= 0) {
|
|
// 判死
|
|
destroy(key);
|
|
} else {
|
|
redis.opsForValue().set(key, value, Duration.ofMillis(expire));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置数据
|
|
*
|
|
* @param key 键
|
|
* @param value 值
|
|
* @param ms 毫秒 TTL
|
|
*/
|
|
public void set(K key, V value, long ms) {
|
|
redis.opsForValue().set(key, value, Duration.ofMillis(ms));
|
|
}
|
|
|
|
/**
|
|
* 获取值
|
|
*
|
|
* @param key 键
|
|
* @return 值
|
|
*/
|
|
public V get(K key) {
|
|
return redis.opsForValue().get(key);
|
|
}
|
|
|
|
/**
|
|
* 获取值,强转为 String
|
|
*
|
|
* @param key 键
|
|
* @return 值
|
|
*/
|
|
public String getString(K key) {
|
|
return get(key).toString();
|
|
}
|
|
|
|
/**
|
|
* 获取值,强转为 Boolean
|
|
*
|
|
* @param key 键
|
|
* @return 值
|
|
*/
|
|
public Boolean is(K key) {
|
|
return Boolean.parseBoolean(getString(key));
|
|
}
|
|
|
|
/**
|
|
* 获取值,强转为 Boolean 并取反
|
|
*
|
|
* @param key 键
|
|
* @return 值
|
|
*/
|
|
public Boolean not(K key) {
|
|
return !is(key);
|
|
}
|
|
|
|
/**
|
|
* 是否存在
|
|
*
|
|
* @param key 键
|
|
* @return true 为存在
|
|
*/
|
|
public boolean has(K key) {
|
|
return get(key) != null;
|
|
}
|
|
|
|
/**
|
|
* 对列表添加值
|
|
*
|
|
* @param key 键
|
|
* @param value 值
|
|
*/
|
|
public void add(K key, V value) {
|
|
redis.opsForList().leftPush(key, value);
|
|
}
|
|
|
|
/**
|
|
* 对列表批量添加值
|
|
*
|
|
* @param key 键
|
|
* @param values 值
|
|
*/
|
|
@SafeVarargs
|
|
public final void addAll(K key, V... values) {
|
|
redis.opsForList().leftPushAll(key, values);
|
|
}
|
|
|
|
/**
|
|
* 获取为列表
|
|
*
|
|
* @param key 键
|
|
* @return 列表
|
|
*/
|
|
public List<V> getList(K key) {
|
|
return redis.opsForList().range(key, 0, -1);
|
|
}
|
|
|
|
/**
|
|
* 获取所有数据列表
|
|
*
|
|
* @return 所有数据列表
|
|
*/
|
|
public Map<K, List<V>> getAllList() {
|
|
Map<K, List<V>> r = new HashMap<>();
|
|
List<K> ks = keys("*");
|
|
for (K k : ks) {
|
|
r.put(k, getList(k));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* 值为列表时查找是否存在某值
|
|
*
|
|
* @param key 键
|
|
* @param value 值
|
|
* @return true 为存在
|
|
*/
|
|
public boolean contains(K key, V value) {
|
|
return getList(key).contains(value);
|
|
}
|
|
|
|
/**
|
|
* 获取所有值
|
|
*
|
|
* @return 所有值
|
|
* @throws TimiException 异常
|
|
*/
|
|
public List<V> values() {
|
|
List<V> r = new ArrayList<>();
|
|
List<K> ks = keys("*");
|
|
for (K k : ks) {
|
|
r.add(get(k));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* 获取所有数据(包括键)
|
|
*
|
|
* @return 所有数据(包括键)
|
|
*/
|
|
public Map<K, V> map() {
|
|
Map<K, V> r = new HashMap<>();
|
|
List<K> ks = keys("*");
|
|
for (K k : ks) {
|
|
r.put(k, get(k));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* 获取符合条件的 key
|
|
*
|
|
* @param pattern 表达式
|
|
* @return keys
|
|
*/
|
|
public List<K> keys(String pattern) {
|
|
List<K> keys = new ArrayList<>();
|
|
scan(pattern, item -> {
|
|
if (item != null) {
|
|
keys.add(serializer.deserialize(item));
|
|
}
|
|
});
|
|
return keys;
|
|
}
|
|
|
|
/**
|
|
* 销毁对象
|
|
*
|
|
* @param key 键
|
|
* @return true 为成功
|
|
*/
|
|
public boolean destroy(K key) {
|
|
if (TimiJava.isNotEmpty(key) && has(key)) {
|
|
return redis.delete(key);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** 删库 */
|
|
public void flushAll() {
|
|
Objects.requireNonNull(redis.getConnectionFactory()).getConnection().serverCommands().flushAll();
|
|
}
|
|
|
|
/**
|
|
* scan 实现
|
|
*
|
|
* @param pattern 表达式
|
|
* @param consumer 对迭代到的 key 进行操作
|
|
*/
|
|
private void scan(String pattern, Consumer<byte[]> consumer) {
|
|
redis.execute((RedisConnection connection) -> {
|
|
try (Cursor<byte[]> cursor = connection.keyCommands().scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
|
|
cursor.forEachRemaining(consumer);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
}
|