简易Redis工具集(待优化、扩充)
主要内容
使用注解配置的多库Redis:
- 基础配置;
- redis常用操作方法;
一个springboot的简单集成
主要maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
yml配置文件
spring:
redis:
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
host: 127.0.0.1
port: 6379
# 使用数据库0,商城小程序使用的是5
database: 0
password: 123456
# 其他配置项已在商城后端设定
# 公共数据库,用于需要同步的数据
redis-public:
database: 5
配置项父类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class BaseConfig {
@Value("${spring.redis.jedis.pool.max-active}")
private int redisPoolMaxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
private int redisPoolMaxWait;
@Value("${spring.redis.jedis.pool.max-idle}")
private int redisPoolMaxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int redisPoolMinIdle;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(redisPoolMaxIdle);
poolConfig.setMinIdle(redisPoolMinIdle);
poolConfig.setMaxTotal(redisPoolMaxActive);
poolConfig.setMaxWaitMillis(redisPoolMaxWait);
return poolConfig;
}
public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig poolConfig, int database) {
// 单机版jedis
RedisStandaloneConfiguration standa = new RedisStandaloneConfiguration();
// 设置redis服务器的host或者ip地址
standa.setHostName(host);
standa.setPort(port);
standa.setPassword(RedisPassword.of(password));
standa.setDatabase(database);
// 获得默认的连接池构造器
// 指定jedisPoolConifig来修改默认的连接池构造器
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb = JedisClientConfiguration.builder().usePooling().poolConfig(poolConfig);
// 通过构造器来构造jedis客户端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
// 单机配置 + 客户端配置 = jedis连接工厂
return new JedisConnectionFactory(standa, jedisClientConfiguration);
}
}
数据库1配置
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@EnableCaching
public class RedisConfig extends BaseConfig {
@Value("${spring.redis.database}")
private int database;
@Bean(name = "baseJedisPoolconfig")
public JedisPoolConfig jedisPoolConfig() {
return super.jedisPoolConfig();
}
/**
* 统一配置RedisTemplate的序列化方式,注入时应该使用@Autowrite。需要使用redis的实体类需要实现Serializable接口<br/>
* 使用hash等集合类型存储对象时,对象内部如果存在自定义实体类,那么这些自定义实体类都需要实现Serializable接口<br/>
* 由于String存储于redis的占用空间一般会比较大,建议都是用hash、list、set等形式存储
*
* @return
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory(this.jedisPoolConfig(), database));
// 以{ "key": value }形式序列化Redis存储对象
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
// 开启事务支持
template.setEnableTransactionSupport(true);
return template;
}
}
数据库5配置
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class PublicRedisConfig extends BaseConfig {
@Value("${spring.redis-public.database}")
private int database;
@Bean(name = "publicJedisPoolConfig")
public JedisPoolConfig publicJedisPoolConfig() {
return jedisPoolConfig();
}
/**
* 公用数据源
*
* @return
*/
@Bean(name = "publicRedisTemplate")
public RedisTemplate<String, Object> publicRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory(publicJedisPoolConfig(), database));
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
template.setEnableTransactionSupport(true);
return template;
}
}
操作工作集接口
import com.alibaba.fastjson.JSONObject;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface BaseRedisService {
RedisTemplate<String, Object> getRedisTemplate();
/**
* 是否存在key
*
* @param key
* @return
*/
boolean hasKey(String key);
/**
* 获取string值
*
* @param key
* @return
*/
Object getString(String key);
/**
* 保存string值
*
* @param key
* @param value
*/
void setString(String key, Object value);
/**
* 把实体类保存为hash记录
*
* @param key hash关键字
* @param t 实体类对象,必须实现java.io.Serializable接口或继承实现了此接口的父类
* @param clear 是否需要删除原有hash键
* @param excepts 需要忽略的字段名
*/
<T> void saveEntityToHash(String key, T t, boolean clear, String... excepts);
/**
* 把异步请求结果保存为hash记录
*
* @param response 请求返回结果,统一使用com.alibaba.fastjson.JSONObject
* @param key 保存的hash关键字
* @param t 实体类对象,必须实现java.io.Serializable接口或继承实现了此接口的父类
* @param clear 是否需要删除原有hash键
*/
<T> void saveEntityToHash(JSONObject response, String key, T t, boolean clear);
/**
* 保存hash值
*
* @param hkey hash值的一级key
* @param mkey hash值的内部key,这个方法只能保存内部key类型为string的数据
* @param value 需要存储的值
* @param <T>
*/
<T> void saveHashValue(String hkey, String mkey, T value);
/**
* 查询整个hash并反射到实体类
*
* @param key
*/
<T> void setHashToEntity(T t, String key, String... excepts);
/**
* 获取原始的hash数据,可作为数据请求接口返回值
*
* @param key
* @return
*/
Map<String, Object> getHashMapResult(String key);
/**
* 获取hash具体参数
*
* @param hkey hash值的一级key
* @param mkey hash值的内部key,这个方法只能查询内部key类型为string的数据
* @param <T>
* @return
*/
<T> T getHashValue(String hkey, String mkey);
/**
* 把hash的值转成com.alibaba.fastjson.JSONObject
*
* @param key
* @return
*/
JSONObject getJsonObjectHashResult(String key);
/**
* 获取list
*
* @param key
* @return
*/
<T> List<T> getList(String key);
/**
* 保存list
*
* @param list
* @param key
*/
<T> void saveList(List<T> list, String key);
/**
* 添加参数到指定list
*
* @param t
* @param key
*/
<T> void addObjToList(T t, String key);
/**
* 删除单个key
*
* @param key
*/
void remove(String key);
/**
* 批量删除字符串形式的key,目前都使用String作为key的类型
*
* @param keys
*/
void remove(Set<String> keys);
/**
* 模糊查找pattern匹配的key,返回所有匹配的key<br/>
* 不使用redisTemplate.keys()做查找的原因是,keys命令会造成redis阻塞<br/>
* 而scan的缺陷是查找结果有可能在cursor迭代过程中被修改,因此这个方法更适用于查找后删除keys
*
* @param pattern 需要匹配的key字符串
* @param count 匹配多少个,默认1000,对目前的需求已满足
* @param unmatch 反向匹配,即不包含,不传或false时默认是匹配返回字符串结果
* @return
*/
Set<String> scan(String pattern, Long count, Boolean unmatch);
}
操作工作集实现类,只展示其一,方法实现有待优化,不同实现类代码基本重复
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
/**
* 手动控制事务,不使用Spring的注解
*/
@Service
public class RedisServiceImpl implements BaseRedisService {
// 唯一区别是选择不同的redis配置
@Resource(name = "redisTemplate")
private RedisTemplate<String, Object> redisTemplate;
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
@Override
public boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
private static boolean containns(String[] array, String key) {
for (Object item : array) {
if (item.equals(key)) return true;
}
return false;
}
@Override
public Object getString(String key) {
return redisTemplate.opsForValue().get(key);
}
@Override
public void setString(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public <T> void saveEntityToHash(String key, T t, boolean clear, String... excepts) {
redisTemplate.multi();
if (clear)
remove(key);
try {
BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : properties) {
String fieldname = property.getName();
// 跳过class属性和自定义的属性
if (fieldname.equals("class") || containns(excepts, fieldname))
continue;
Object value = property.getReadMethod().invoke(t);
// System.out.println(fieldname + ": " + value);
// 当前属性的值不为空时,保存到redis
if (null != value)
redisTemplate.opsForHash().put(key, fieldname, value);
}
redisTemplate.exec();
// 开启事务支持后,需要手动释放redis连接
RedisConnectionUtils.unbindConnection(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public <T> void saveEntityToHash(JSONObject response, String key, T t, boolean clear) {
redisTemplate.multi();
if (clear)
remove(key);
try {
BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : properties) {
String fieldname = property.getName();
// 跳过class属性和自定义的属性
if (fieldname.equals("class"))
continue;
Object value = response.get(fieldname);
// 当前属性的值不为空时,保存到redis
if (null != value)
redisTemplate.opsForHash().put(key, fieldname, value);
}
redisTemplate.exec();
// 开启事务支持后,需要手动释放redis连接
RedisConnectionUtils.unbindConnection(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
@Override
public <T> void saveHashValue(String hkey, String mkey, T value) {
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
hashOperations.put(hkey, mkey, value);
}
@Override
public <T> void setHashToEntity(T t, String key, String... excepts) {
Map<Object, Object> res = redisTemplate.opsForHash().entries(key);
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(t.getClass());
PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : properties) {
String fieldname = property.getName();
// 跳过自定义属性
if (containns(excepts, fieldname))
continue;
if (res.containsKey(fieldname)) {
Method setter = property.getWriteMethod();
setter.invoke(t, res.get(fieldname));
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public Map<String, Object> getHashMapResult(String key) {
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
return hashOperations.entries(key);
}
@Override
public <T> T getHashValue(String hkey, String mkey) {
HashOperations<String, String, T> hashOperations = redisTemplate.opsForHash();
return hashOperations.get(hkey, mkey);
}
@Override
public JSONObject getJsonObjectHashResult(String key) {
Map<String, Object> current = getHashMapResult(key);
if (null != current && !current.isEmpty())
return new JSONObject(current);
return null;
}
@Override
public <T> List<T> getList(String key) {
if (!hasKey(key))
return null;
long size = redisTemplate.opsForList().size(key);
return (List<T>) redisTemplate.opsForList().range(key, 0, size);
}
@Override
public <T> void saveList(List<T> list, String key) {
for (T t : list) {
redisTemplate.opsForList().rightPush(key, t);
}
}
@Override
public <T> void addObjToList(T t, String key) {
redisTemplate.opsForList().rightPush(key, t);
}
@Override
public void remove(String key) {
redisTemplate.delete(key);
}
@Override
public void remove(Set<String> keys) {
redisTemplate.delete(keys);
}
@Override
public Set<String> scan(String pattern, Long count, Boolean unmatch) {
Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) conn -> {
Set<String> temps = new HashSet<>();
String match = "*" + pattern + "*";
if (null != unmatch && unmatch)
match = "*";
Cursor<byte[]> cursor = conn.scan(new ScanOptions.ScanOptionsBuilder().match(match).count(null != count ? count : 1000).build());
while (cursor.hasNext()) {
String key = new String(cursor.next());
if (null != unmatch && unmatch) {
if (key.indexOf(pattern) < 0) {
temps.add(key);
}
} else {
temps.add(key);
}
}
return temps;
});
return keys;
}
}