springboot版本:2.7.6
集成redis
1. 添加依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 單節(jié)點(diǎn)設(shè)置啟用連接池時(shí)添加 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 添加配置
spring:
redis:
database: 0
password: yx123123=
host: 192.168.110.108
port: 6379
client-type: lettuce
lettuce:
pool:
# 連接耗盡時(shí)挨决,最多等待10秒
max-wait: 10s
# 連接池中最小空閑連接數(shù)
min-idle: 4
# 空閑對(duì)象逐出器運(yùn)行間隔時(shí)長(zhǎng)
time-between-eviction-runs: 10s
# 啟用池
enabled: true
3. Java Configuration
/**
* redis 連接工廠
*/
@Autowired
private RedisConnectionFactory connectionFactory;
/**
* 實(shí)例化RedisTemplate,使用Json序列化弓颈,并對(duì)日期/時(shí)間進(jìn)行格式化,統(tǒng)一使用字符串key
*
* @return RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
ObjectMapper objectMapper = new ObjectMapper();
// 日期格式支持
JavaTimeModule timeModule = new JavaTimeModule();
final ZoneId DEFAULT_ZONE_ID = ZoneId.of("CTT", SHORT_IDS);
final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DEFAULT_ZONE_ID);
final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DEFAULT_ZONE_ID);
timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER)).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)).addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER)).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
objectMapper.registerModule(timeModule);
# 反序列化時(shí)忽略對(duì)象中不存在的屬性
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
# 反序列化時(shí)可以轉(zhuǎn)為對(duì)象
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
// key統(tǒng)一使用字符串
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// value統(tǒng)一使用json
template.setValueSerializer(genericJackson2JsonRedisSerializer);
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
template.setConnectionFactory(connectionFactory);
return template;
}
集成cache
1. 添加依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. 添加配置
spring:
cache:
type: REDIS
redis:
# 配置緩存時(shí)長(zhǎng)15分鐘
time-to-live: 15m
cache-null-values: false
enable-statistics: true
# 緩存key的前綴
key-prefix: 'cache:${spring.application.name}:'
3. 代碼
3.1 Java Configuration
@EnableCaching
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
/**
* redis 連接工廠
*/
private final RedisConnectionFactory connectionFactory;
/**
* 緩存配置數(shù)據(jù)
*/
private final CacheProperties cacheProperties;
public RedisCacheAutoConfiguration(RedisConnectionFactory connectionFactory, CacheProperties cacheProperties) {
this.connectionFactory = connectionFactory;
this.cacheProperties = cacheProperties;
}
@Bean
@Override
public CacheManager cacheManager() {
CacheProperties.Redis redisCacheProperties = cacheProperties.getRedis();
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(connectionFactory).cacheDefaults(redisCacheConfiguration(redisCacheProperties)).transactionAware().cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)));
if (redisCacheProperties.isEnableStatistics()) {
builder.enableStatistics();
}
return builder.build();
}
/**
* 自定義KeyGenerator
* key結(jié)構(gòu) key-prefix+cacheName::類(lèi)名.方法名(參數(shù))
* 最終生成的key會(huì)受到key-prefix和@Cacheable注解中配置的cacheNames影響 示例:
* <b>@Cacheable(cacheNames = {"query.hello"})</b>
* query.hello::HelloController.hello(spring)
* 如果配置文件中配置了key-prefix: 'cache:'
* cache:query.hello::HelloController.hello(world)
*
* @return KeyGenerator
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
return target.getClass().getSimpleName() + "." + method.getName() + paramStr;
};
}
/**
* redis cache configuration 配置
* 如果需要緩存LocalDateTime類(lèi)型穴张,自定義value序列化器
*
* @param redisCacheProperties redis緩存配置
* @return RedisCacheConfiguration
*/
RedisCacheConfiguration redisCacheConfiguration(CacheProperties.Redis redisCacheProperties) {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisCacheValueSerializer()));
if (redisCacheProperties.getTimeToLive() != null) {
cacheConfig = cacheConfig.entryTtl(redisCacheProperties.getTimeToLive());
}
if (redisCacheProperties.getKeyPrefix() != null) {
cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
}
if (!redisCacheProperties.isCacheNullValues()) {
cacheConfig = cacheConfig.disableCachingNullValues();
}
if (!redisCacheProperties.isUseKeyPrefix()) {
cacheConfig = cacheConfig.disableKeyPrefix();
}
// 這里會(huì)影響最終生成的緩存key
if (StringUtils.hasText(redisCacheProperties.getKeyPrefix())) {
cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
}
return cacheConfig;
}
}
3.2 自定義value序列化器
/**
* spring cache redis 序列化器
* 用redis緩存spring cache時(shí)使用前弯,解決了LocalDateTime序列化和反序列化問(wèn)題
*
* @author co
* @since 2023-12-05 16:37:00
*/
public class RedisCacheValueSerializer extends GenericJackson2JsonRedisSerializer {
public RedisCacheValueSerializer() {
super();
}
@Override
public Object deserialize(byte[] source) throws SerializationException {
Object value = super.deserialize(source);
if (value instanceof String) {
String str = value == null ? "" : (String) value;
// 以下為L(zhǎng)ocalDate和LocalDateTime的判斷和轉(zhuǎn)換,可以替換為其他更具效率的方式
try {
if (str.length() == 10) {
return LocalDate.parse(str, DATE_FORMATTER);
}
if (str.length() == 19) {
return LocalDateTime.parse(str, DATE_TIME_FORMATTER);
}
} catch (DateTimeParseException ex) {
}
}
return value;
}
@Override
public byte[] serialize(Object source) throws SerializationException {
if (source instanceof LocalDateTime) {
return super.serialize(((LocalDateTime) source).format(DATE_TIME_FORMATTER));
}
if (source instanceof LocalDate) {
return super.serialize(((LocalDate) source).format(DATE_FORMATTER));
}
return super.serialize(source);
}
}
完整示例
1. pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 單節(jié)點(diǎn)設(shè)置啟用連接池時(shí)添加 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- spring cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. yaml
spring:
application:
name: app-name
cache:
type: REDIS
redis:
time-to-live: 15m
cache-null-values: false
enable-statistics: true
key-prefix: 'cache:${spring.application.name}:'
redis:
database: 0
password: yx123123=
host: 192.168.110.108
client-type: lettuce
lettuce:
pool:
max-wait: 10s
min-idle: 4
time-between-eviction-runs: 10s
enabled: true
3. Java代碼
3.1 config
/**
* Redis & Cache相關(guān)配置類(lèi)
* 配置了自定義KeyGenerator砰蠢,因此必須繼承CachingConfigurerSupport辨泳,否則不生效
*
* @author co
* @since 2023-12-05 10:19:48
*/
@EnableCaching
@Configuration
@Import({RedisClient.class})
@EnableConfigurationProperties(CacheProperties.class)
public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
/**
* redis 連接工廠
*/
private final RedisConnectionFactory connectionFactory;
/**
* 緩存配置數(shù)據(jù)
*/
private final CacheProperties cacheProperties;
public RedisCacheAutoConfiguration(RedisConnectionFactory connectionFactory, CacheProperties cacheProperties) {
this.connectionFactory = connectionFactory;
this.cacheProperties = cacheProperties;
}
/**
* 實(shí)例化RedisTemplate虱岂,使用Json序列化,并對(duì)日期/時(shí)間進(jìn)行格式化菠红,統(tǒng)一使用字符串key
*
* @return RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
ObjectMapper objectMapper = new ObjectMapper();
// 日期格式支持
final ZoneId DEFAULT_ZONE_ID = ZoneId.of("CTT", SHORT_IDS);
final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DEFAULT_ZONE_ID);
final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DEFAULT_ZONE_ID);
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER)).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)).addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER)).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
objectMapper.registerModule(timeModule);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
// key統(tǒng)一使用字符串
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// value統(tǒng)一使用json
template.setValueSerializer(genericJackson2JsonRedisSerializer);
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
template.setConnectionFactory(connectionFactory);
return template;
}
@Bean
@Override
public CacheManager cacheManager() {
CacheProperties.Redis redisCacheProperties = cacheProperties.getRedis();
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(connectionFactory).cacheDefaults(redisCacheConfiguration(redisCacheProperties)).transactionAware().cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)));
if (redisCacheProperties.isEnableStatistics()) {
builder.enableStatistics();
}
return builder.build();
}
/**
* 自定義KeyGenerator
* key結(jié)構(gòu) key-prefix+cacheName::類(lèi)名.方法名(參數(shù))
* 最終生成的key會(huì)受到key-prefix和@Cacheable注解中配置的cacheNames影響 示例:
* <b>@Cacheable(cacheNames = {"query.hello"})</b>
* query.hello::HelloController.hello(spring)
* 如果配置文件中配置了key-prefix: 'cache:'
* cache:query.hello::HelloController.hello(world)
*
* @return KeyGenerator
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
return target.getClass().getSimpleName() + "." + method.getName() + paramStr;
};
}
/**
* redis cache configuration 配置
* 如果需要緩存LocalDateTime等類(lèi)型第岖,需要在pom.xml中引入以下依賴(lài),可根據(jù)錯(cuò)誤提示進(jìn)行配置
* <dependency>
* <groupId>com.fasterxml.jackson.datatype</groupId>
* <artifactId>jackson-datatype-jsr310</artifactId>
* </dependency>
*
* @param redisCacheProperties redis緩存配置
* @return RedisCacheConfiguration
*/
RedisCacheConfiguration redisCacheConfiguration(CacheProperties.Redis redisCacheProperties) {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisCacheValueSerializer()));
if (redisCacheProperties.getTimeToLive() != null) {
cacheConfig = cacheConfig.entryTtl(redisCacheProperties.getTimeToLive());
}
if (redisCacheProperties.getKeyPrefix() != null) {
cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
}
if (!redisCacheProperties.isCacheNullValues()) {
cacheConfig = cacheConfig.disableCachingNullValues();
}
if (!redisCacheProperties.isUseKeyPrefix()) {
cacheConfig = cacheConfig.disableKeyPrefix();
}
// 這里會(huì)影響最終生成的緩存key
if (StringUtils.hasText(redisCacheProperties.getKeyPrefix())) {
cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
}
return cacheConfig;
}
}
3.2 redis工具類(lèi)
/**
* Redis工具類(lèi)
* <p>
* 包含常用操作及獲取鎖
*
* @author co
* @since 2023-12-07 10:05:20
*/
public final class RedisClient {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定緩存失效時(shí)間
* (命令EXPIRE)
*
* @param key 鍵
* @param time 時(shí)間(秒)
* @return true / false
*/
public Boolean expire(String key, int time) {
if (time > 0) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return false;
}
/**
* 指定緩存失效時(shí)間
* (命令EXPIRE)
*
* @param key 鍵
* @param time 時(shí)間(秒)
* @param timeUnit 時(shí)間單位
* @return true / false
*/
public Boolean expire(String key, long time, TimeUnit timeUnit) {
if (time > 0) {
return redisTemplate.expire(key, time, timeUnit);
}
return false;
}
/**
* 根據(jù) key 獲取過(guò)期時(shí)間
* (命令TTL)
*
* @param key 鍵
* @return 秒
*/
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判斷 key 是否存在
* (命令EXISTS)
*
* @param key 鍵
* @return true / false
*/
public Boolean exists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 刪除緩存
* 不存在key不會(huì)拋異常
*
* @param keys 集合
*/
public void delete(Collection<String> keys) {
redisTemplate.delete(keys);
}
/**
* 刪除緩存
* (命令DEL)
*
* @param key 鍵(一個(gè)或者多個(gè))
*/
public void delete(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(Arrays.asList(key));
}
}
}
/**
* 按表達(dá)式刪除緩存
*
* @param pattern 表達(dá)式
*/
public void deleteAll(String pattern) {
if (pattern == null || pattern.length() == 0) {
throw new IllegalArgumentException("參數(shù)不正確");
}
redisTemplate.delete(scan(pattern));
}
/**
* 查詢所有滿足表達(dá)式的key
* (命令KEYS试溯,實(shí)際是利用SCAN命令實(shí)現(xiàn)蔑滓,不會(huì)產(chǎn)生阻塞)
*
* @param pattern 表達(dá)式
* @return key
*/
@SuppressWarnings("all")
@NonNull
public Set<String> scan(String pattern) {
if (pattern == null || pattern.length() == 0) {
throw new IllegalArgumentException("參數(shù)不正確");
}
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keys = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(pattern).count(1000).build());
while (cursor.hasNext()) {
keys.add(new String(cursor.next()));
}
return keys;
}
);
}
// ============================== String ==============================
/**
* 放入緩存
*
* @param key 鍵
* @param value 值
*/
public void set(String key, Object value, long timeout, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 放入緩存
*
* @param key 鍵
* @param value 值
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 獲取緩存(string數(shù)據(jù)結(jié)構(gòu))
* <p>
* 通過(guò)泛型T指定緩存數(shù)據(jù)類(lèi)型
*
* @param key 鍵
* @return 值
* @see #getMap
*/
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return key == null ? null : (T) redisTemplate.opsForValue().get(key);
}
/**
* 緩存普通鍵值對(duì),并設(shè)置失效時(shí)間
*
* @param key 鍵
* @param value 值
* @param seconds 時(shí)間(秒)遇绞,如果 time <= 0 則不設(shè)置失效時(shí)間
*/
public void set(String key, Object value, int seconds) {
if (seconds > 0) {
redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(key, seconds);
}
}
/**
* 當(dāng)key不存在時(shí)放入鍵值對(duì)
* 如果已經(jīng)存在key返回false
*
* @param key 鍵
* @param value 值
* @return true/false
*/
public Boolean setNx(String key, Object value) {
return redisTemplate.opsForValue().setIfAbsent(key, value);
}
/**
* 當(dāng)key不存在時(shí)放入鍵值對(duì)键袱,并在指定時(shí)間后自動(dòng)刪除
* 如果已經(jīng)存在key返回false
*
* @param key 鍵
* @param value 值
* @return true/false
*/
public Boolean setNx(String key, Object value, int time) {
return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
}
/**
* 遞增
* 如果不存在key將自動(dòng)創(chuàng)建
* (命令I(lǐng)NCR)
*
* @param key 鍵
* @param increment 遞增大小
* @return 遞增后的值
*/
@SuppressWarnings("unchecked")
public Long increment(String key, Long increment) {
return redisTemplate.opsForValue().increment(key, increment);
}
/**
* 遞增1
* 如果不存在key將自動(dòng)創(chuàng)建
* (命令I(lǐng)NCR)
*
* @param key 鍵
* @return 遞增后的值
*/
public Long increment(String key) {
return redisTemplate.opsForValue().increment(key, 1);
}
/**
* 遞減
* 如果不存在key將自動(dòng)創(chuàng)建
* (命令DECR)
*
* @param key 鍵
* @param decrement 遞減大小
* @return 遞減后的值
*/
@SuppressWarnings("unchecked")
public <T extends Number> T decrement(String key, T decrement) {
return (T) redisTemplate.opsForValue().increment(key, -decrement.doubleValue());
}
/**
* 遞減1
* 如果不存在key將自動(dòng)創(chuàng)建
* (命令DECR)
*
* @param key 鍵
* @return 遞減后的值
*/
public long decrement(String key) {
return decrement(key, 1L);
}
// ============================== Map ==============================
/**
* 往指定HashMap中添加一對(duì)鍵值對(duì),key不存在將自動(dòng)創(chuàng)建
* (命令HMSET)
*
* @param name HashMap的名字
* @param key 添加的鍵
* @param value 添加的值
*/
public void putMap(String name, String key, Object value) {
redisTemplate.opsForHash().put(name, key, value);
}
/**
* 往指定HashMap中添加一對(duì)鍵值對(duì)摹闽,key不存在將自動(dòng)創(chuàng)建,key存在不進(jìn)行任何操作
* (命令HSETNX)
*
* @param name HashMap的名字
* @param key 添加的鍵
* @param value 添加的值
*/
public Boolean putMapIfAbsent(String name, String key, Object value) {
return redisTemplate.opsForHash().putIfAbsent(name, key, value);
}
/**
* 往指定HashMap中添加另一個(gè)map
* (命令HMSET)
*
* @param name HashMap的名字
* @param map 值
*/
public void putMap(String name, Map<String, ?> map) {
redisTemplate.opsForHash().putAll(name, map);
}
/**
* 從指定HashMap中獲取指定key的值
* (命令HGET)
*
* @param mapName HashMap的名字(no null)
* @param key HashMap中的鍵(no null)
* @param <T> 根據(jù)實(shí)際類(lèi)型自定義
* @return 值
*/
@SuppressWarnings("unchecked")
public <T> T getMapItem(String mapName, String key) {
return (T) redisTemplate.opsForHash().get(mapName, key);
}
/**
* 從指定HashMap中獲取多個(gè)key的值
* (命令HMGET)
*
* @param mapName HashMap的名字(no null)
* @param key HashMap中的鍵蹄咖,傳多個(gè)(no null)
* @param <T> 根據(jù)實(shí)際類(lèi)型自定義
* @return list
*/
public <T> List<T> getMapItems(String mapName, String... key) {
return redisTemplate.<String, T>opsForHash().multiGet(mapName, Arrays.asList(key));
}
/**
* 獲取指定的HashMap
* (命令HGETALL)
*
* @param mapName HashMap的名字(no null)
* @return HashMap
*/
public <T> Map<String, T> getMap(String mapName) {
return redisTemplate.<String, T>opsForHash().entries(mapName);
}
/**
* 刪除 HashMap 中的值
* (命令HDEL)
*
* @param mapName HashMap的名字
* @param keys HashMap中的key(可以多個(gè),no null)
*/
public void deleteHash(String mapName, Object... keys) {
redisTemplate.opsForHash().delete(mapName, keys);
}
/**
* 判斷指定 HashMap 中是否含有指定key
*
* @param hashMapName HashMap的名字(no null)
* @param key HashMap中的key(no null)
* @return true / false
*/
public Boolean exists(String hashMapName, String key) {
return redisTemplate.opsForHash().hasKey(hashMapName, key);
}
/**
* map 遞增指定值
*
* @param hashMapName HashMap的名字(no null)
* @param key HashMap中的key(no null)
* @return true / false
*/
public Long incrementMap(String hashMapName, String key, long delta) {
return redisTemplate.opsForHash().increment(hashMapName, key, delta);
}
/**
* 是否存在hashkey
*
* @param hashMapName HashMap的名字(no null)
* @param hashKey HashMap中的key(no null)
* @return true / false
*/
public Boolean hasMapKey(String hashMapName, Object hashKey) {
return redisTemplate.opsForHash().hasKey(hashMapName, hashKey);
}
// ============================== zset ==============================
/**
* 添加zset元素 有則覆蓋
*
* @param key
* @param v
* @param score
* @return
*/
public <T> Boolean zAdd(String key, T v, long score) {
return redisTemplate.opsForZSet().add(key, v, score);
}
/**
* 添加zset元素 有則覆蓋
*
* @param key
* @param start
* @param end
* @return
*/
public Long zRemove(String key, long start, long end) {
return redisTemplate.opsForZSet().removeRange(key, start, end);
}
/**
* zset 元素個(gè)數(shù)
*
* @param key
* @return
*/
public Long zSize(String key) {
return redisTemplate.opsForZSet().size(key);
}
/**
* 自增zset score
*
* @param key
* @param v
* @param delta
* @return
*/
public <T> Double zIncrementScore(String key, T v, Double delta) {
return redisTemplate.opsForZSet().incrementScore(key, v, delta);
}
/**
* 獲取指定key的所有元素
*
* @param key
* @param <T>
* @return
*/
public <T> Set<T> zAll(String key) {
return (Set<T>) redisTemplate.opsForZSet().range(key, 0, -1);
}
/**
* 獲取指定位置元素
*
* @param key
* @param index
* @param <T>
* @return
*/
public <T> T zGet(String key, int index) {
Set<Object> objects = redisTemplate.opsForZSet().range(key, index, index + 1L);
if (CollectionUtils.isEmpty(objects)) return null;
return (T) objects.iterator().next();
}
/**
* zset刪除指定元素
*
* @param key
* @param v
* @return
*/
public Long zDel(String key, Object v) {
return redisTemplate.opsForZSet().remove(key, v);
}
// ============================== set ==============================
public Boolean sIsMember(String key, Object obj) {
return redisTemplate.opsForSet().isMember(key, obj);
}
/**
* 新增set 元素
*
* @param key
* @param v
* @return
*/
public <T> Long sAdd(String key, T v) {
return redisTemplate.opsForSet().add(key, v);
}
/**
* 獲取set元素
*
* @param key
* @return
*/
public <T> Set<T> sGet(String key) {
return (Set<T>) redisTemplate.opsForSet().members(key);
}
/**
* 新增set元素
*
* @param key
* @param values
*/
public <T> void sAdd(String key, T... values) {
redisTemplate.opsForSet().add(key, values);
}
/**
* 移除set元素
*
* @param key
* @param v
*/
public <T> void srm(String key, T v) {
redisTemplate.opsForSet().remove(key, v);
}
// ============================== list ==============================
/**
* 從左邊塞入元素
*
* @param key
* @param v
*/
public <T> Long lPush(String key, T v) {
return redisTemplate.opsForList().leftPush(key, v);
}
/**
* 從右邊批量塞入元素
*
* @param key
* @param vs
*/
public <T> Long rPushAll(String key, T... vs) {
return redisTemplate.opsForList().rightPushAll(key, vs);
}
/**
* 刪除右邊的元素
*
* @param key
* @param removeCount
*/
public void rightRemove(String key, Long removeCount) {
redisTemplate.opsForList().trim(key, 0, listSize(key) - removeCount);
}
/**
* list集合中元素個(gè)數(shù)
*
* @param key
*/
public Long listSize(String key) {
return redisTemplate.opsForList().size(key);
}
/**
* 批量查詢
*
* @param key
*/
public <T> List<T> list(String key) {
return (List<T>) redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 獲取redisTemplate钩骇,可實(shí)現(xiàn)更多操作
*
* @return RedisTemplate
*/
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public <T> T execute(RedisScript<T> script, List<String> keys, Object... args) {
return redisTemplate.execute(script, keys, args);
}
/**
* 管道
*
* @param consumer
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> List<T> piPipelined(Consumer<RedisOperations<Object, Object>> consumer) {
return (List<T>) redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
consumer.accept(operations);
return null;
}
});
}
// ============================== bitmap ==============================
/**
* bitmap設(shè)置指定位置 為 1或0
* (命令 SETBIT)
*
* @param key key
* @param offset 偏移量
* @param value true / false
*/
public boolean setBit(String key, long offset, boolean value) {
return redisTemplate.opsForValue().setBit(key, offset, value);
}
/**
* bitmap獲取指定位置結(jié)果
* (命令 GETBIT)
*
* @param key key
* @param offset 偏移量
* @return true / false
*/
public boolean getBit(String key, long offset) {
return redisTemplate.opsForValue().getBit(key, offset);
}
/**
* bitmap 統(tǒng)計(jì)和比藻,所有位置為1的總數(shù)量
* (命令 BITCOUNT)
*
* @param key key
*/
public long bitCount(String key) {
return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
}
public long bitPos(String key, boolean bit, Range<Long> range) {
return redisTemplate.execute((RedisCallback<Long>) con -> con.bitPos(key.getBytes(), bit, range));
}
/**
* bitField
* (命令 BITFIELD)
*
* @param key key
* @param bitFieldSubCommands demo: BitFieldSubCommands bitFieldSubCommands =BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.UINT_16).valueAt(0);
*/
public List<Long> bitField(String key, BitFieldSubCommands bitFieldSubCommands) {
return redisTemplate.opsForValue().bitField(key, bitFieldSubCommands);
}
@Nullable
public RedisConnection getConnection() {
final RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
if (connectionFactory == null) return null;
return connectionFactory.getConnection();
}
}
3.3 自定義緩存值序列化器
/**
* spring cache redis 序列化器
* 用redis緩存spring cache時(shí)使用铝量,解決了LocalDateTime序列化和反序列化問(wèn)題
*
* @author co
* @since 2023-12-05 16:37:00
*/
public class RedisCacheValueSerializer extends GenericJackson2JsonRedisSerializer {
public RedisCacheValueSerializer() {
super();
}
@Override
public Object deserialize(byte[] source) throws SerializationException {
Object value = super.deserialize(source);
if (value instanceof String) {
String str = value == null ? "" : (String) value;
// 以下為L(zhǎng)ocalDate和LocalDateTime的判斷和轉(zhuǎn)換倘屹,可以替換為其他更具效率的方式
try {
if (str.length() == 10) {
return LocalDate.parse(str, DATE_FORMATTER);
}
if (str.length() == 19) {
return LocalDateTime.parse(str, DATE_TIME_FORMATTER);
}
} catch (DateTimeParseException ex) {
}
}
return value;
}
@Override
public byte[] serialize(Object source) throws SerializationException {
if (source instanceof LocalDateTime) {
return super.serialize(((LocalDateTime) source).format(DATE_TIME_FORMATTER));
}
if (source instanceof LocalDate) {
return super.serialize(((LocalDate) source).format(DATE_FORMATTER));
}
return super.serialize(source);
}
}