简介
- 本案例解决了为全网最强mybaties二级缓存解决方案,搞过mybaties二级缓存的朋友应该知道,mybaties自带的二级缓存只会对对同一个namespace下的生效,例如A
namespace下对一个表执行了delete,update,insert任何一个操作修改了表数据之后,B namespace
select该表的sql的缓存并不会清除,只会把A
namespace下对应的select该表的查询清除,从而造成脏读现象,本案例重新实现了mybaties二级缓存的逻辑,完全解决脏读问题。
properties.setProperty(“opencache”, “1”);//是否开启缓存1开始,2关闭
- 本案例实现了,mybaties拦截器进行分页的操作,用户只需要定义一个page类,拦截器可进行自动拦截之后分页
例如 Page page = new Page(1, 3);
- 本案例实现了多租户的功能,仅需在插件里配置多租户的字段和值,即可实现多租户功能,用户开发过程中,就不需要关注多租户字段了例如
properties.setProperty(“tenantId”, “tenant_id”);//租户字段
properties.setProperty(“tenantValue”, “1”);//租户值
以下为代码,如有问题,请留言指出。
注册插件
/**
* 功能描述:注册插件
*
* @author chenyan
* @date 2019/6/4 10:54
*/
@EnableTransactionManagement
@Configuration
@MapperScan({"com.hyit.system.demo.dao"})
public class MapperConfig {
// @Value(value = "${sql.tenantId}")
// private String tenantId;
// @Value(value = "${sql.tenantValue}")
// private String tenantValue;
// @Value(value = "${sql.opencache}")
// private String opencache;
//注册插件
@Bean
public PageInterceptor myPlugin() {
PageInterceptor myPlugin = new PageInterceptor();
//设置参数,比如阈值等,可以在配置文件中配置
Properties properties = new Properties();
properties.setProperty("opencache", "1");//是否开启缓存1开始,2关闭
properties.setProperty("tenantId", "tenant_id");//租户字段
properties.setProperty("tenantValue", "1");//租户值
myPlugin.setProperties(properties);
return myPlugin;
}
}
分页拦截器
/**
* 功能描述:mybaties分页拦截器,实现二级缓存跨namespace存取
* , Connection.class
*
* @author chenyan
* @date 2019/6/4 10:54
*/
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
public class PageInterceptor implements Interceptor {
public static final Logger logger = LoggerFactory.getLogger(PageInterceptor.class);
private RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
//开启二级缓存
private String opencache;
//多租户id
private String tenantId;
//多租户id
private String t;
//多租户值
private String tenantValue;
@Override
public Object intercept(Invocation invocation) throws Throwable {
doInvocation(invocation);
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
// 在配置插件的时候配置默认参数
@Override
public void setProperties(Properties properties) {
String opencache = properties.getProperty("opencache");
String tenantId = properties.getProperty("tenantId");
String tenantValue = properties.getProperty("tenantValue");
this.opencache = opencache;
this.tenantId = tenantId;
this.tenantValue = tenantValue;
}
//基于mycat方式
private Object mycatInvocation(Invocation invocation) throws Throwable {
long stime = System.currentTimeMillis();
StatementHandler statementHandler = getActuralHandlerObject(invocation);
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
String sql = statementHandler.getBoundSql().getSql();
//二级缓存方案
if ("1".equals(this.opencache)) {
if (checkSql(sql)) {
try {
Set<TableStat.Name> nameSet = sqlParser(sql);
nameSet.stream().forEach(t -> {
Set<String> keys = redisTemplate.keys("*:*" + t + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 检测通过,是select语句
if (checkIsSelectFalg(sql)) {
//分页方案
BoundSql boundSql = statementHandler.getBoundSql();
Object paramObject = boundSql.getParameterObject();
Page pageParam = getPageParam(paramObject);
if (pageParam == null) {
Object o = invocation.proceed();
long etime = System.currentTimeMillis();
logger.info((etime - stime) + "");
return o;
} else {