二级缓存
@CacheNamespace(implementation = MybatisEhcacheCache.class)
一、导入依赖
org.mybatis.caches
mybatis-ehcache
1.1.0
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
完整的 pom
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.4.RELEASE
com.xxtsoft
call-center
0.0.1-SNAPSHOT
call-center
信讯通呼叫中心标准版
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity5
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-validation
com.baomidou
mybatis-plus-boot-starter
3.4.0
com.baomidou
mybatis-plus-generator
3.4.0
org.freemarker
freemarker
2.3.30
cn.hutool
hutool-all
5.4.4
com.alibaba
fastjson
1.2.73
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
org.apache.poi
poi-ooxml
4.1.1
poi-ooxml-schemas
org.apache.poi
org.apache.poi
poi-ooxml-schemas
3.17
org.apache.commons
commons-lang3
3.11
junit
junit
test
org.webjars
webjars-locator
0.34
org.webjars
jquery
2.1.4
org.webjars
bootstrap
3.3.6
jquery
org.webjars
org.webjars
font-awesome
4.4.0
org.webjars.npm
animate.css
4.1.1
org.webjars.bower
bootstrap-table
1.14.1
org.webjars
zTree
3.5.18
jquery
org.webjars
org.webjars.npm
bootstrap-select
1.13.14
org.webjars.bower
metisMenu
1.1.3
org.webjars.bower
bootstrap
org.webjars
jQuery-slimScroll
1.3.1
jquery
org.webjars
org.webjars
pace
1.0.2
org.webjars
jquery-cookie
1.4.1
jquery
org.webjars
org.mybatis.caches
mybatis-ehcache
1.1.0
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
org.webjars.bower
jquery.serializeJSON
2.6.1
org.webjars
echarts
2.1.10
org.webjars
layui
2.5.6
org.webjars.bowergithub.sentsin
layer
3.1.1
org.webjars
vue
2.6.11
org.webjars.npm
axios
0.20.0
org.webjars.npm
element-ui
2.14.0
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-surefire-plugin
2.12.4
-Xmx1024m
二、设置配置文件 application.yml
# 连接数据库
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.6.250:3306/call_center?sserverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
# url: jdbc:mysql://10.8.0.142:3306/call_center?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: XXT123456.
devtools:
restart:
# spring boot devtools 热部署后访问报 404 问题
# https://www.jianshu.com/p/9337fd517291
poll-interval: 3000ms
quiet-period: 2999ms
# 配置 redis 缓存
redis:
# host: 10.8.0.142
host: 192.168.6.250
port: 6379
password: XXT123456.
database: 1
timeout: 2000ms
# 连接超时时间(毫秒)默认是2000ms
lettuce:
pool:
max-active: 200
# 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 100
# 连接池中的最大空闲连接
min-idle: 50
# 连接池中的最小空闲连接
shutdown-timeout: 100ms
# MyBatis plus 显示 SQL 语句
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 使用缓存
cache-enabled: true
# ===================================================================
# 日志配置 从低到高
# log.trace("trace");
# log.debug("debug");
# log.info("info");
# log.warn("warn");
# log.error("error");
# ===================================================================
logging:
# 对 com.xxtsoft 下的文件开启 trace 级别的日志
level:
com:
xxtsoft: trace
server:
port: 8081
servlet:
context-path: /call
三、创建 RedisUtil
封装 RedisTemplateRedisTemplate
package com.xxtsoft.util.redis;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* redis 工具类
*
* @author yang
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return boolean
*/
public 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 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public Long getExpire(String key) {
final Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
assert expire != null;
return expire;
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key) {
try {
final Boolean aBoolean = redisTemplate.hasKey(key);
assert aBoolean != null;
return aBoolean;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
if (Validator.isNotNull(key) && Validator.isTrue(key.length > 0)) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(ListUtil.toList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public Boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return long
*/
public Long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(大于0)
* @return long
*/
public Long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map
四、SpringContextUtils
:维护 Spring 中的实例
可以使用 hutool 的 Spring 工具 - SpringUtil 来替换
package com.xxtsoft.util.spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* Spring 工具类
*
* @author yang
*/
@Component
public class SpringContextUtils implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*
* @return 获取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取HttpServletRequest
*/
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}
public static String getDomain() {
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin() {
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
/**
* 通过name获取 Bean.
*
* @param name name
* @return Bean
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz 类
* @param 类型
* @return bean
*/
public static T getBean(Class clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name 名字
* @param clazz 类
* @param 类型
* @return bean
*/
public static T getBean(String name, Class clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
五、RedisConfig
:配置Redis
的 RedisTemplate
和 CacheManager
并载入 Spring IOC
容器
package com.xxtsoft.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import javax.annotation.Resource;
import java.time.Duration;
import static java.util.Collections.singletonMap;
/**
* redis 配置类
*
* @author yang
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;
/**
* RedisTemplate 配置
*
* @param lettuceConnectionFactory lettuceConnectionFactory
* @return RedisTemplate
*/
@Bean
public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 设置序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
om.enableDefaultTyping(DefaultTyping.NON_FINAL);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer> stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash key序列化
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 缓存配置管理器
*
* @param factory factory
* @return CacheManager
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
// 配置序列化(缓存默认有效期 6小时)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
/* 自定义配置test:demo 的超时时间为 5分钟*/
return RedisCacheManager
.builder(RedisCacheWriter.lockingRedisCacheWriter(factory))
.cacheDefaults(redisCacheConfiguration)
.withInitialCacheConfigurations(singletonMap("test:demo", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
.transactionAware()
.build();
}
}
六、创建 MybatisRedisCache
实现 org.apache.ibatis.cache.Cache
package com.xxtsoft.config;
import cn.hutool.extra.spring.SpringUtil;
import com.xxtsoft.util.redis.RedisUtil;
import lombok.Setter;
import org.apache.ibatis.cache.Cache;
import org.springframework.stereotype.Component;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 缓存实现类
*
* @author 杨磊
*/
@Component
public class MybatisRedisCache implements Cache {
private RedisUtil redisUtil;
private RedisUtil getRedis() {
return SpringUtil.getBean(RedisUtil.class);
}
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 缓存刷新时间(秒)
*/
@Setter
private long flushInterval = 0L;
private String id;
public MybatisRedisCache() {
}
public MybatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object o, Object o1) {
getRedis().hset(getId(), o.toString(), o1);
if (flushInterval > 0L) {
getRedis().expire(getId(), flushInterval);
}
}
@Override
public Object getObject(Object o) {
return getRedis().hget(getId(), o.toString());
}
@Override
public Object removeObject(Object o) {
return getRedis().hdel(getId(), o);
}
@Override
public void clear() {
getRedis().del(getId());
}
@Override
public int getSize() {
return getRedis().hsize(getId());
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
七、添加 缓存注解
Mybatis 支持两种方式添加缓存注解,以下方案二选一即可:
- 添加缓存注解
@CacheNamespace
在代码中为每个 mapper 添加缓存注解,声明 implementation 或 eviction 的值为 MybatisRedisCache
@CacheNamespace(implementation = MybatisRedisCache.class,eviction=MybatisRedisCache.class)
public interface UserMapper extends BaseMapper {
}
- 在对应的 mapper.xml 中将原有注释修改为链接式声明,以保证 xml 文件里的缓存能够正常
本篇文章由一文多发平台ArtiPub自动发布