maven 依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
1.配置文件
#redis 集群节点信息
spring.redis.cluster.nodes=10.3.4.184:6379,10.3.4.184:6380,10.3.4.184:6381,10.3.4.185:6382,10.3.4.185:6383,10.3.4.185:6384
#redis 命令超时时间
spring.redis.commandTimeout=5000
spring.redis.password=
#redis最大连接数
spring.redis.pool.max-total=30
# redis最大空闲连接数
spring.redis.pool.max-idle=10
# redis最小空闲连接数
spring.redis.pool.min-idle=5
# redis 最大活跃时间
spring.redis.pool.max-active=5000
# redis 连接最大等待时间
spring.redis.pool.max-wait=1500
# 每次逐出检查时,逐出的最大数目
spring.redis.pool.numTestsPerEvictionRun=1024
# 逐出扫描的时间间隔(毫秒)如果为负数,则不运行逐出线程,默认-1
spring.redis.pool.timeBetweenEvictionRunsMillis=30000
# 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
spring.redis.pool.minEvictableIdleTimeMillis=1800000
# 对象空闲多久后逐出,当空闲时间> 该值 且空闲连接 > 最大空闲数 ,不再根据minEvictableIdleTimeMillis 判断
spring.redis.pool.softMinEvictableIdleTimeMills=10000
# 获取连接时检查有效性,默认false
spring.redis.pool.testOnBorrow=true
# 在空闲时检查有效性,默认false
spring.redis.pool.testWhileIdle=true
spring.redis.pool.blockWhenExhausted=false
2.增加配置类,加载配置到配置类RedisConfig.java(@EnableCaching 注解驱动的缓存管理功能 )
package com.cfcc.bigdata.base.config.redis;
import com.cfcc.bigdata.base.config.cache.AbstractCacheConfig;
import com.cfcc.bigdata.common.base.mto.MessageEntity;
import com.cfcc.bigdata.voucher.thread.MessageQueue;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashMap;
import java.util.Map;
/**
* Created by gzy
*/
@Configuration
@EnableCaching
public class RedisConfig extends AbstractCacheConfig {
@Value("${spring.redis.cluster.nodes}")
private String nodes;
@Value("${spring.redis.commandTimeout}")
private int commandTimeout;
@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.max-wait}")
private long maxWait;
@Value("${spring.redis.pool.max-active}")
private long maxActive;
@Value("${spring.redis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.pool.numTestsPerEvictionRun}")
private int numTestsPerEvictionRun;
@Value("${spring.redis.pool.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMilliss;
@Value("${spring.redis.pool.minEvictableIdleTimeMillis}")
private long minEvictableIdleTimeMillis;
@Value("${spring.redis.pool.softMinEvictableIdleTimeMills}")
private long softMinEvictableIdleTimeMills;
@Value("${spring.redis.pool.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.redis.pool.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.redis.pool.blockWhenExhausted}")
private boolean blockWhenExhausted;
/*对象池配置*/
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWait);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMilliss);
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMilliss);
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMills);
jedisPoolConfig.setTestOnBorrow(testOnBorrow);
jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);
jedisPoolConfig.setTestWhileIdle(testWhileIdle);
return jedisPoolConfig;
}
/* @Bean
public JedisCluster getJedisCluster() {
String[] nNodes = nodes.split(",");
Set<HostAndPort> nodes = new HashSet<>();
//分割集群节点
for (String node : nNodes) {
String[] hp = node.split(":");
nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
}
return new JedisCluster(nodes, commandTimeout,jedisPoolConfig());
}*/
/**
* 通过反射获取JedisCluster
* @param factory
* @return
*/
@Bean
public JedisCluster redisCluster(RedisConnectionFactory factory) {
Object obj = null;
try {
obj = getFieldValueByObject(factory, "cluster");
} catch (Exception e) {
}
return (JedisCluster)obj;
}
@Bean(name = "clusterConfig")
public RedisClusterConfiguration clusterConfig() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
String[] nNodes = nodes.split(",");
//分割集群节点
for (String node : nNodes) {
String[] hp = node.split(":");
RedisNode redis = new RedisNode(hp[0], Integer.parseInt(hp[1]));
redisClusterConfiguration.addClusterNode(redis);
}
return redisClusterConfiguration;
}
@Bean(name = "factory")
public RedisConnectionFactory jedisConnectionFactory(RedisClusterConfiguration clusterConfiguration) {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(clusterConfiguration);
jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
jedisConnectionFactory.setTimeout(30000);
jedisConnectionFactory.setUsePool(true);
return jedisConnectionFactory;
}
/**
* 自定义RedisTemplate,修改序列化方式
*
* @param factory
* @return
*/
@Bean(name = "sqlCachRedisTemplate")
@Primary
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
final StringRedisTemplate redisTemplate = new StringRedisTemplate(redisConnectionFactory);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setValueSerializer(getJsonSerializer());
redisTemplate.setHashKeySerializer(redisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean(name = "msgRedisTemplate")
public RedisTemplate<String, MessageEntity> messageRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, MessageEntity> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setHashKeySerializer(redisSerializer);
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setValueSerializer(getJsonSerializer());
redisTemplate.setHashValueSerializer(getJsonSerializer());
//开启事务支持,这个事务有点鸡肋不支持回滚
redisTemplate.setEnableTransactionSupport(true);
return redisTemplate;
}
/**
* 缓存分页查询的数据
* @param factory
* @return
*/
@Bean(name = "pageCacheRedisTemplate")
public RedisTemplate<String, Object> redisPageTemplate(RedisConnectionFactory factory) {
final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setValueSerializer(getJsonSerializer());
redisTemplate.setHashKeySerializer(redisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 定义Redis的缓存管理器
* @return
*/
@Bean
public CacheManager redisCacheManager(@Qualifier("sqlCachRedisTemplate") RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//设置默认缓存过期时间是7Day
redisCacheManager.setDefaultExpiration(7 * 24 * 60 * 60);
Map<String, Long> expiresMap = new HashMap<>();
//设置sql 的缓存时间 30 M
expiresMap.put("paramCache", 1800l);
redisCacheManager.setExpires(expiresMap);
return redisCacheManager;
}
/**
* 设置数据序列化方式为json
*
* @return
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private Jackson2JsonRedisSerializer<Object> getJsonSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
AbstractCacheConfig.java (这里对sql 查询结果生成缓存的Key的方法进行了重写 )
package com.cfcc.bigdata.base.config.cache;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* @author gzy
* @version : 1.0
* @date : 2018/11/17 0017
*/
public abstract class AbstractCacheConfig extends CachingConfigurerSupport {
/**
* 继承并修改缓存key的生成方式
*/
@SuppressWarnings("rawtypes")
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(".");
sb.append(method.getName());
for (Object param : params) {
if (param instanceof List) {
sb.append("#[");
List paramList = (List) param;
for (Object paramObj : paramList) {
sb.append("&" + ToStringBuilder.reflectionToString(paramObj, ToStringStyle.SHORT_PREFIX_STYLE));
}
sb.append("]");
} else if (param instanceof Map) {
sb.append("#[");
Map paramMap = (Map) param;
for (Object key : paramMap.keySet()) {
Object value = paramMap.get(key);
sb.append("&");
sb.append(ToStringBuilder.reflectionToString(key, ToStringStyle.SHORT_PREFIX_STYLE));
sb.append(":");
sb.append(ToStringBuilder.reflectionToString(value, ToStringStyle.SHORT_PREFIX_STYLE));
}
sb.append("]");
} else {
sb.append("#" + ToStringBuilder.reflectionToString(param, ToStringStyle.SHORT_PREFIX_STYLE));
}
}
return sb.toString();
}
};
}
public Object getFieldValueByObject(Object object, String targetFieldName) throws IllegalAccessException {
// 获取该对象的Class
Class objClass = object.getClass();
// 获取所有的属性数组
Field[] fields = objClass.getDeclaredFields();
for (Field field : fields) {
//属性名称
field.setAccessible(true);
String currentFieldName = field.getName();
if (currentFieldName.equals(targetFieldName)) {
return field.get(object);
}
}
return null;
}
}
3.使用缓存(所有的Service 继承此类就可以使用缓存管理功能了)
package com.cfcc.bigdata.common.base.service;
import com.cfcc.bigdata.common.base.helper.PageQueryHelper;
import com.cfcc.bigdata.common.base.vo.PropertyFilter;
import com.cfcc.bigdata.common.base.mybatis.base.BaseMapper;
import com.cfcc.bigdata.common.base.vo.EasyUIDatagridParam;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 所有Service的父类,提供通用CURD方法的默认实现。
*
*/
@Transactional
@CacheConfig(cacheNames = "paramCache")
public abstract class BaseCacheService<T extends Serializable> {
@Autowired
private BaseMapper<T> mapper;
/**
* 根据实体中的属性值进行查询,查询条件使用等号
*
* @param record
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public List<T> select(T record) {
return mapper.select(record);
}
/**
* 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
*
* @param key
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public T selectByPrimaryKey(Object key) {
return mapper.selectByPrimaryKey(key);
}
/**
* 查询全部结果,select(null)方法能达到同样的效果
*
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public List<T> selectAll() {
return mapper.selectAll();
}
/**
* 根据条件分页查询记录列表
*
* @param page
* 当前页码
* @param rows
* 每页记录数
* @param record
* 条件对象
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public PageInfo<T> selectPage(Integer page, Integer rows, T record) {
PageHelper.startPage(page, rows);
List<T> list = mapper.select(record);
return new PageInfo<>(list);
}
/**
* 根据条件分页查询记录列表
*
* @param page
* 当前页码
* @param rows
* 每页记录数
* @param example
* 条件对象
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public PageInfo<T> selectPageByExample(Integer page, Integer rows, Example example) {
PageHelper.startPage(page, rows);
List<T> list = mapper.selectByExample(example);
return new PageInfo<>(list);
}
/**
* 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
*
* @param record
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public T selectOne(T record) {
return mapper.selectOne(record);
}
/**
* 根据实体中的属性查询总数,查询条件使用等号
*
* @param record
* @return
*/
@Transactional(readOnly = true)
public int selectCount(T record) {
return mapper.selectCount(record);
}
/**
* 根据主键字符串进行查询,类中只有存在一个带有@Id注解的字段
* <p>
* 通过操作ids字符串进行操作,ids 如 "1,2,3" 这种形式的字符串,这个方法要求实体类中有且只有一个带有@Id注解的字段,否则会抛出异常
* </p>
*
* @param ids
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public List<T> selectByIds(String ids) {
if (ids != null && !"".equals(ids)) {
List<T> list = new ArrayList<>();
String[] idArray = ids.split(",");
for (String id : idArray) {
list.add(mapper.selectByPrimaryKey(id));
}
return list;
}
return Collections.emptyList();
}
/**
* 根据Example条件进行查询
* <p>
* 重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
* </p>
*
* @param example
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public List<T> selectByExample(Object example) {
return mapper.selectByExample(example);
}
/**
* 根据Example条件进行查询总数
*
* @param example
* @return
*/
@Transactional(readOnly = true)
public int selectCountByExample(Object example) {
return mapper.selectCountByExample(example);
}
/**
* 根据通用条件进行分页查询
*
* @param param
* easyui传来的参数信息
* @param filters
* 通用条件对象
* @param entityClass
* 查询的实体对象类型
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public PageInfo<T> selectEasyUIPageByFilters(final EasyUIDatagridParam param, final List<PropertyFilter> filters, Class<?> entityClass) {
PageQueryHelper pageQueryHelper = new PageQueryHelper();
Example example = pageQueryHelper.generateExampleByFilters(param, filters, entityClass);
return selectPageByExample(param.getPage(), param.getRows(), example);
}
/**
* 根据通用条件查询所有符合条件记录
*
* @param param
* easyui传来的参数信息
* @param filters
* 通用条件对象
* @param entityClass
* 查询的实体对象类型
* @return
*/
@Cacheable
@Transactional(readOnly = true)
public List<T> selectEasyUIAllByFilters(final EasyUIDatagridParam param, final List<PropertyFilter> filters, Class<?> entityClass) {
PageQueryHelper pageQueryHelper = new PageQueryHelper();
Example example = pageQueryHelper.generateExampleByFilters(param, filters, entityClass);
return mapper.selectByExample(example);
}
/**
* 保存一个实体,null的属性也会保存,不会使用数据库默认值
* beforeInvocation 在执行sql之前清除缓存
* @param record
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int insert(T record) {
return mapper.insert(record);
}
/**
* 保存一个实体,null的属性不会保存,会使用数据库默认值
*
* @param record
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int insertSelective(T record) {
return mapper.insertSelective(record);
}
/**
* 批量插入,支持批量插入的数据库可以使用,例如MySQL,H2等
*
* @param recordList
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int insertList(List<T> recordList) {
return mapper.insertListUseAllCols(recordList);
}
/**
* 根据主键更新实体全部字段,null值会被更新
*
* @param record
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int updateByPrimaryKey(T record) {
return mapper.updateByPrimaryKey(record);
}
/**
* 根据主键更新属性不为null的值
*
* @param record
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int updateByPrimaryKeySelective(T record) {
return mapper.updateByPrimaryKeySelective(record);
}
/**
* 根据Example条件更新实体record包含的全部属性,null值会被更新
*
* @param record
* @param example
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int updateByExample(T record, Object example) {
return mapper.updateByExample(record, example);
}
/**
* 根据Example条件更新实体record包含的不是null的属性值
*
* @param record
* @param example
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int updateByExampleSelective(T record, Object example) {
return mapper.updateByExampleSelective(record, example);
}
/**
* 批量更新
*
* @param recordList
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public void updateList(List<T> recordList) {
for(T t : recordList) {
mapper.updateByPrimaryKey(t);
}
}
/**
* 根据实体属性作为条件进行删除,查询条件使用等号
*
* @param record
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int delete(T record) {
return mapper.delete(record);
}
/**
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
* <p>
* 通过操作ids字符串进行操作,ids 如 "1,2,3" 这种形式的字符串,这个方法要求实体类中有且只有一个带有@Id注解的字段,否则会抛出异常
* </p>
*
* @param ids
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int deleteByIds(String ids) {
if (ids != null && !"".equals(ids)) {
String[] idArray = ids.split(",");
for (String id : idArray) {
mapper.deleteByPrimaryKey(id);
}
return idArray.length;
}
return 0;
}
/**
* 根据主键字段进行删除,方法参数必须包含完整的主键属性
*
* @param key
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int deleteByPrimaryKey(Object key) {
return mapper.deleteByPrimaryKey(key);
}
/**
* 根据Example条件删除数据
*
* @param example
* @return
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public int deleteByExample(Object example) {
return mapper.deleteByExample(example);
}
/**
* 刷新缓存
*/
@CacheEvict(beforeInvocation = true, allEntries = true)
public void reload() {
}
}