Redis——(自定义工具类&Redis的Java配置类)
一、Redis自定义工具类
1.1 工具类
/**
* @Description 操作redis键值的工具类
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/***************key的通用操作**************************/
/**
* [key]设置 key 的过期时间
* @param key 键
* @param time 过期时间(秒)
* @return 操作是否成功
*/
private 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]根据 key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒)
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* [key]判断 key 是否存在
* @param key 键
* @return true 存在;false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* [key]删除指定的 key
* @param keys 可以传一个或多个待删除的key
*/
public void del(String... keys) {
if (keys != null && keys.length > 0) {
if (keys.length == 1) {
redisTemplate.delete(keys[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(keys));
}
}
}
/***************String类型操作**************************/
/**
* [String]根据 key 获取值其 value
* @param key 键
* @return 键对应的值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.boundValueOps(key).get();
}
/**
* [String]根据key 存入 value
* @param key 操作的键
* @param value 值
*/
public void set(String key, Object value) {
redisTemplate.boundValueOps(key).set(value);
}
/**
* [String]根据key 存入 value,并设置过期时间
* @param key 键
* @param value 值
* @param time 过期时间(秒)
*/
public void set(String key, Object value, long time) {
redisTemplate.boundValueOps(key).set(value, time, TimeUnit.SECONDS);
}
/**
* [String]为键 key 储存的数字值加上一。
* 1.如果键 key 不存在, 那么它的值会先被初始化为 0 , 然后再执行 INCR 命令。
* 2.如果键 key 储存的值不能被解释为数字, 那么 INCR 命令将返回一个错误。
*
* @param key 键
* @param data 要增加的数值(>=1)
* @return 该命令会返回键 key 在执行加一操作之后的值。
*/
public long incr(String key, Long data) {
BoundValueOperations opt = redisTemplate.boundValueOps(key);
try {
int i = Integer.parseInt(data.toString());
if (i <=0){
throw new RuntimeException("递增数值要大于1,且不能为负数");
}else {
return redisTemplate.boundValueOps(key).increment(data);
}
} catch (NumberFormatException e) {
throw new RuntimeException("值必须是数字,才能执行递增");
}
}
/**
* [String]为键 key 储存的数字值减去一。
* 1.如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 DECR 操作。
* 2.如果键 key 储存的值不能被解释为数字, 那么 DECR 命令将返回一个错误。
*
* @param key 键
* @param data 要减少的数值(>=1)
* @return 该命令会返回键 key 在执行减一操作之后的值。
*/
public long decr(String key, Long data) {
BoundValueOperations opt = redisTemplate.boundValueOps(key);
try {
int i = Integer.parseInt(data.toString());
if (i <= 0){
throw new RuntimeException("递减数值要大于1,且不能为负数");
}else {
return redisTemplate.boundValueOps(key).decrement(data);
}
} catch (NumberFormatException e) {
throw new RuntimeException("值必须是数字,才能执行递减");
}
}
/**
* [String]同时为多个键设置值。
* 1.如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值,
*
* @param keyValueMap
* @return OK
*/
public String mSet(Map<String,Object> keyValueMap){
if (keyValueMap.size() == 0){
throw new RuntimeException("map集合为空!!!");
}else {
for (Map.Entry<String, Object> entry : keyValueMap.entrySet()) {
BoundValueOperations ops = redisTemplate.boundValueOps(entry.getKey());
ops.set(entry.getValue());
}
return "OK";
}
}
/**
* [String]返回给定的一个或多个字符串键和对应的值。(key-value)
*
* @param keyList key的集合
* @return 此命令将返回一个集合列表, 列表中包含了所有给定键和值。
*/
public Map<String,Object> mGet(List<String> keyList){
Map<String,Object> keyValueMap = new LinkedHashMap<>();
for (String key : keyList) {
Object value = redisTemplate.boundValueOps(key).get();
keyValueMap.put(key,value);
}
return keyValueMap;
}
/***************Set类型操作**************************/
/**
* [Set]根据key获取Set中的所有成员
*
* @param key 键
* @return 集合中的所有成员
*/
public Set sMembers(String key) {
try {
return redisTemplate.boundSetOps(key).members();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* [Set]判断 value 元素是否集合 key 的成员。
* 1.如果 member 元素是集合的成员,返回 true 。
* 2.如果 member 元素不是集合的成员,或 key 不存在,返回 false 。
*
* @param key 要查询的 key
* @param value 要查询的key集合中的成员
* @return true或false
*/
public boolean sIsMember(String key, Object value) {
try {
return redisTemplate.boundSetOps(key).isMember(value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* [Set]将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
* 1.假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
* 2.当 key 不是集合类型时,返回一个错误。
*
* @param key 键
* @param values 值 (可以是多个)
* @return 成功个数
*/
public long sAdd(String key, Object... values) {
try {
return redisTemplate.boundSetOps(key).add(values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* [Set]将一个或多个 member 元素加入到集合 key 当中,并设置有效时间,已经存在于集合的 member 元素将被忽略。
* 1.假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
* 2.当 key 不是集合类型时,返回一个错误。
*
* @param key 键
* @param time 过期时间(秒)
* @param values 值(可以有多个)
* @return 成功的个数
*/
public long sAdd(String key, long time, Object... values) {
try {
Long count = redisTemplate.boundSetOps(key).add(values);
this.expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* [Set]返回集合 key 的基数(集合中元素的数量)。
*
* @param key 键
* @return 集合的基数。 当 key 不存在时,返回 0 。
*/
public long sCard(String key) {
try {
return redisTemplate.boundSetOps(key).size();
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* [Set]移除集合 key 中的一个或多个 value 元素,不存在的 value 元素会被忽略。
* 1.当 key 不是集合类型,返回一个错误。
*
* @param key 键
* @param values 值(可以是多个)
* @return 被成功移除的元素的数量,不包括被忽略的元素。
*/
public long sRemove(String key, Object... values) {
try {
Long count = redisTemplate.boundSetOps(key).remove(values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/***************List类型操作**************************/
/**
* [List]从绑定键的列表中获取begin和end之间的元素。
* 1.负数下标:-1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
*
* @param key 键
* @param start 开始下标(从0开始)
* @param end 结束下标(-1:最后一个元素)
* @return
*/
public List lRange(String key, long start, long end) {
try {
return redisTemplate.boundListOps(key).range(start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* [List]获取列表 key 的长度。
* 1.如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
* 2.如果 key 不是列表类型,返回一个错误。
*
* @param key 键
* @return key列表的长度
*/
public long lLen(String key) {
try {
return redisTemplate.boundListOps(key).size();
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* [List]返回列表 key 中,下标为 index 的元素。
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;
* index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return 列表中下标为 index 的元素。 如果 index 参数的值不在列表的区间范围内(out of range),返回 null 。
*/
public Object lIndex(String key, long index) {
try {
return redisTemplate.boundListOps(key).index(index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* [List]将一个或多个值 value 插入到列表 key 的表尾(最右边)。
* 1.如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾。
*
* @param key 键
* @param value 要存入的值(一个或多个)
* @return 执行 RPUSH 操作后,表的长度。
*/
public long lrRushAll(String key, Object... value) {
return redisTemplate.boundListOps(key).rightPushAll(value);
}
/**
* [List]将一个或多个值 value 插入到列表 key 的表尾(最右边)。并设置过期时间。
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public void lrRushAll(String key, long time, Object... value) {
redisTemplate.boundListOps(key).rightPushAll(value);
//设置过期时间
this.expire(key, time);
}
/**
* [List]根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return true/false
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.boundListOps(key).set(index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* [List]移除N个值为value的成员(从左到右)
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.boundListOps(key).remove(count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/***************ZSet类型操作**************************/
/**
* [ZSet]将value添加到绑定键的排序集中,或者如果它已经存在则更新其score 。
*
* @param key 键
* @param value 成员
* @param score 成员的分数
* @return 是否添加成功
*/
public Boolean zAdd(String key,Object value,Long score){
return redisTemplate.boundZSetOps(key).add(value, score);
}
/**
* [ZSet]将value-score的集合添加到绑定键的排序集中,或者如果它已经存在则更新其score 。
* 1.注意value和score的封装格式,伪代码:List<Map<value,score>>
*
* @param key 键
* @param valueScoreList 要添加的 成员-分数 集合
* @return 添加成功的个数
*/
public Long zAdd(String key,List<Map<Object,Long>> valueScoreList){
Long count = 0L;
for (Map<Object,Long> map : valueScoreList) {
for (Map.Entry<Object, Long> entry : map.entrySet()) {
Boolean isAdd = redisTemplate.boundZSetOps(key).add(entry.getKey(), entry.getValue());
if (isAdd){
count++;
}
}
}
return count;
}
/**
* [ZSet]返回有序集 key 中,成员 member 的 score 值。
*
* @param key 键
* @param member 指定键对应集合中的成员
* @return 指定成员的分数
*/
public Double zScore(String key,Object member){
return redisTemplate.boundZSetOps(key).score(member);
}
/***************Hash类型操作**************************/
}
1.2 工具类的使用测试
@SpringBootTest
class SpringbootRedisPracticeApplicationTests {
@Autowired
private RedisUtil redisUtil;
@Test
void contextLoads() {
long count = redisUtil.sadd("city", "wuhan", "hagnzhou", "shagnhai", "anhui");
System.out.println(count); //4
}
}
二、Redis的Java配置类
/**
* @Description redis的配置类,覆盖自动配置的redis配置类中的原有方法
* @Author cb
* @Date 2021-11-22 21:23
**/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
// 在给定对象和 Redis 存储中的底层二进制数据之间执行自动序列化/反序列化。
// 如果使用自动配置类中的RedisTemplate对象,该对象默认对key和value都采用JDK序列化器来序列化
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 构造一个新的RedisTemplate实例。(如果只是操作字符串,还可以用 StringRedisTemplate)
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂。
template.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
/**
* ObjectMapper 提供了读取和写入 JSON 的功能,
* 可以与基本 POJO(Plain Old Java Objects)
* 或通用 JSON 树模型 ( JsonNode ) 之间进行读取和写入,以及用于执行转换的相关功能。
*/
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
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;
}
//Spring 的中央缓存管理器 SPI。
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
/**
* 不指定序列化输入类型时存储到redis中的数据将是没有类型的纯json数据,如:
* [{"id":72,"name":"ZhangSan"}]
*
* 指定序列化输入类型(存储到redis里的数据将是有类型的json数据)
* ["java.util.ArrayList",[{"@class":"com.pojo.user","id":72,"name":"ZhangSan"}]]
* 这样java获取到数据后,将会将数据自动转化为java.util.ArrayList和com.pojo.user,方便直接使用。
*
* NON_FINAL :除final外的的属性信息都需要被序列化和反序列化。
*/
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间 600 秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.
fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
return cacheManager;
}
}
部分有关配置可以参考博客:ObjectMapper的enableDefaultTyping()方法过期解决