1,加入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
database: 0
host: localhost
port: 6379
2,重新定义redis序列化方式
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
/**
* 配置自己的redisTemplate
* StringRedisTemplate 默认使用使用StringRedisSerializer来序列化
* RedisTemplate 默认使用JdkSerializationRedisSerializer来序列化
*/
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//开启默认类型
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"));
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
3,编写mybatis的自定义缓存类
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final String id;
private RedisTemplate redisTemplate;
/**
* 缓存自动过期时间 24小时
*/
private static final long EXPIRE_TIME_IN_HOURS = 24;
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return id;
}
/**
* Put query result to redis
*
* @param key
* @param value
*/
@Override
@SuppressWarnings("unchecked")
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key.toString(), value, EXPIRE_TIME_IN_HOURS, TimeUnit.HOURS);
logger.debug("Put query result to redis");
}
catch (Throwable t) {
logger.error("Redis put failed", t);
}
}
/**
* Get cached query result from redis
*
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
logger.debug("Get cached query result from redis");
return opsForValue.get(key.toString());
}
catch (Throwable t) {
logger.error("Redis get failed, fail over to db", t);
return null;
}
}
/**
* Remove cached query result from redis
*
* @param key
* @return
*/
@Override
@SuppressWarnings("unchecked")
public Object removeObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
logger.debug("Remove cached query result from redis");
}
catch (Throwable t) {
logger.error("Redis remove failed", t);
}
return null;
}
/**
* Clears this cache instance
*/
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute((RedisCallback) connection -> {
connection.flushDb();
return null;
});
logger.debug("Clear all the cached query result from redis");
}
/**
* This method is not used
*
* @return
*/
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}
4,数据接口层添加缓存注解
启用二级缓存可在xml文件启用和mapper上使用@CacheNamespace 这两种方式
只能有一种生效,不能同时使用
因为mapper层通常简单的查询直接用标签实现的或者继承公共方法extends BaseMapper等等,所以推荐使用@CacheNamespace
//开启缓存,默认所有查询都缓存,所有修改删除新增清空缓存
@CacheNamespace(implementation = RedisCache.class)
@Repository
public interface ProductMapper {
/**
* 默认缓存
* @return
*/
@Select("select code,name from product where code=#{id}")
Product select(@Param("id")long id);
/**
* 自动清空缓存
* @return
*/
void update(Product product);
/**
* 指定禁用缓存
* @return
*/
@Options(useCache = false)
@Select("select code,name from product where code=#{id}")
Product select2(@Param("id")long id);
/**
* 某些复杂更新写在xml中的,使用Flush使下次查询刷新缓存
* @return
*/
@Flush
int batchUpdate(@Param("id")long id);
}
5,所有缓存对象必须序列化,并添加无参构造函数
@Data
public class Product implements Serializable {
private static final long serialVersionUID = -5035766641884746376L;
public Product(){}
private String code;
private String name;
}
6,ApplicationContextHolder 保存全局上下文,用于获取bean
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
applicationContext = ctx;
}
/**
* Get application context from everywhere
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* Get bean by class
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
/**
* Get bean by class name
*
* @param name
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}