Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.jeesuite.mybatis.plugin.cache.CacheHandler Maven / Gradle / Ivy
package com.jeesuite.mybatis.plugin.cache;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.persistence.Id;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Invocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.jeesuite.mybatis.core.BaseEntity;
import com.jeesuite.mybatis.core.InterceptorHandler;
import com.jeesuite.mybatis.core.InterceptorType;
import com.jeesuite.mybatis.kit.ReflectUtils;
import com.jeesuite.mybatis.parser.EntityInfo;
import com.jeesuite.mybatis.parser.MybatisMapperParser;
import com.jeesuite.mybatis.plugin.cache.annotation.Cache;
import com.jeesuite.mybatis.plugin.cache.annotation.CacheEvictCascade;
import com.jeesuite.mybatis.plugin.cache.name.DefaultCacheMethodDefine;
import com.jeesuite.mybatis.plugin.cache.name.Mapper3CacheMethodDefine;
import com.jeesuite.mybatis.plugin.cache.provider.DefaultCacheProvider;
/**
* 自动缓存拦截处理
* @description
* @author vakin
* @date 2015年12月7日
* @Copyright (c) 2015, jwww
*/
public class CacheHandler implements InterceptorHandler,InitializingBean,DisposableBean {
protected static final String SPLIT_PONIT = ".";
private static final String REGEX_PLACEHOLDER = ":%s";
private static final String REGEX_POINT = "\\.";
protected static final String GROUPKEY_SUFFIX = "~keys";
protected static final Logger logger = LoggerFactory.getLogger(CacheHandler.class);
//需要缓存的所有mapper
private static List cacheEnableMappers = new ArrayList<>();
private static Map mapperNameRalateEntityNames = new HashMap<>();
/**
* 更新方法关联的缓存组
*/
private static Map> cacheEvictCascades = new HashMap<>();
private static Map> queryCacheMethods = new HashMap<>();
private static Map updateCacheMethods = new HashMap<>();
private static List groupKeys = new ArrayList<>();
//记录当前线程写入的所有缓存key
private static ThreadLocal> TransactionWriteCacheKeys = new ThreadLocal<>();
protected static CacheProvider cacheProvider;
private CacheMethodDefine methodDefine;
//CRUD框架驱动 default,mapper3
private String crudDriver = "default";
private ScheduledExecutorService clearExpiredGroupKeysTimer;
public void setCacheProvider(CacheProvider cacheProvider) {
CacheHandler.cacheProvider = cacheProvider;
}
public void setCrudDriver(String crudDriver) {
this.crudDriver = crudDriver;
}
@Override
public Object onInterceptor(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement mt = (MappedStatement)args[0];
if(mt.getSqlCommandType().equals(SqlCommandType.SELECT)){
//按主键查询
QueryMethodCache cacheInfo = getQueryMethodCache(mt.getId());
if(cacheInfo == null)return null;
final String cacheKey = genarateQueryCacheKey(cacheInfo.keyPattern, args[1]);
//按主键查询以及标记非引用关系的缓存直接读取缓存
if(cacheInfo.isPk || !cacheInfo.uniqueResult){
//从缓存读取
Object object = cacheProvider.get(cacheKey);
if(object != null){
object = new ArrayList<>(Arrays.asList(object));
if(logger.isDebugEnabled())logger.debug("method[{}] find result from cacheKey:{}",mt.getId(),cacheKey);
}
return object;
}else{
//新根据缓存KEY找到与按ID缓存的KEY
String cacheKeyById = cacheProvider.getStr(cacheKey);
if(cacheKeyById == null)return null;
Object object = cacheProvider.get(cacheKeyById);
if(object != null){
object = new ArrayList<>(Arrays.asList(object));
if(logger.isDebugEnabled())logger.debug("method[{}] find result from cacheKey:{} ,ref by:{}",mt.getId(),cacheKeyById,cacheKey);
}
return object;
}
}
return null;
}
@SuppressWarnings("rawtypes")
@Override
public void onFinished(Invocation invocation,Object result) {
Object[] args = invocation.getArgs();
MappedStatement mt = (MappedStatement)args[0];
String mapperNameSpace = mt.getId().substring(0, mt.getId().lastIndexOf(SPLIT_PONIT));
QueryMethodCache cacheInfo = null;
if(mt.getSqlCommandType().equals(SqlCommandType.SELECT)){
if(result == null)return;
if((cacheInfo = getQueryMethodCache(mt.getId())) == null)return;
if(result instanceof List){
List list = (List)result;
if(list.isEmpty())return;
result = cacheInfo.uniqueResult ? list.get(0) : result;
}
final String cacheKey = genarateQueryCacheKey(cacheInfo.keyPattern, args[1]);
//按主键查询以及标记非引用关系的缓存直接读取缓存
if(cacheInfo.isPk || !cacheInfo.uniqueResult){
if(cacheProvider.set(cacheKey,result, cacheInfo.expire)){
if(logger.isDebugEnabled())logger.debug("method[{}] put result to cache,cacheKey:{}",mt.getId(),cacheKey);
}
//结果为集合的情况,增加key到cacheGroup
if(!cacheInfo.uniqueResult){
cacheProvider.putGroupKeys(cacheInfo.cacheGroupKey, cacheKey,cacheInfo.expire);
logger.debug("method[{}] add key:[{}] to group key:[{}]",mt.getId(),cacheInfo.cacheGroupKey, cacheKey);
}else{
//
cacheUniqueSelectRef(result, mt, cacheKey);
}
}else{
//之前没有按主键的缓存,增加按主键缓存
String idCacheKey = genarateQueryCacheKey(getQueryByPkMethodCache(mt.getId()).keyPattern,result);
if(idCacheKey != null && cacheKey != null && cacheProvider.set(idCacheKey,result, cacheInfo.expire)
&& cacheProvider.set(cacheKey,idCacheKey, cacheInfo.expire)){
if(logger.isDebugEnabled())logger.debug("method[{}] put result to cache,cacheKey:{},and add ref cacheKey:{}",mt.getId(),idCacheKey,cacheKey);
}
}
}else{
//返回0,未更新成功
if(result != null && ((int)result) == 0)return;
if(updateCacheMethods.containsKey(mt.getId())){
String cacheByPkKey = null;
UpdateByPkMethodCache updateMethodCache = updateCacheMethods.get(mt.getId());
if(updateMethodCache.sqlCommandType.equals(SqlCommandType.DELETE)){
cacheByPkKey = genarateQueryCacheKey(updateMethodCache.keyPattern,args[1]);
cacheProvider.remove(cacheByPkKey);
if(logger.isDebugEnabled())logger.debug("method[{}] remove cacheKey:{} from cache",mt.getId(),cacheByPkKey);
}else{
cacheByPkKey = genarateQueryCacheKey(updateMethodCache.keyPattern,args[1]);
boolean insertCommond = mt.getSqlCommandType().equals(SqlCommandType.INSERT);
if(insertCommond || mt.getSqlCommandType().equals(SqlCommandType.UPDATE)){
if(result != null){
QueryMethodCache queryByPkMethodCache = getQueryByPkMethodCache(mt.getId());
cacheProvider.set(cacheByPkKey,args[1], queryByPkMethodCache.expire);
if(logger.isDebugEnabled())logger.debug("method[{}] update cacheKey:{}",mt.getId(),cacheByPkKey);
//插入其他唯一字段引用
if(insertCommond)cacheUniqueSelectRef(args[1], mt, cacheByPkKey);
//
addCurrentThreadCacheKey(cacheByPkKey);
}
}
}
}
//删除同一cachegroup关联缓存
removeCacheByGroup(mt.getId(), mapperNameSpace);
}
}
/**
* 删除缓存组
* @param mt
* @param mapperNameSpace
*/
private void removeCacheByGroup(String msId, String mapperNameSpace) {
//删除cachegroup关联缓存
if(!cacheEnableMappers.contains(mapperNameSpace))return;
String entityName = mapperNameRalateEntityNames.get(mapperNameSpace);
String cacheGroup = entityName + GROUPKEY_SUFFIX;
cacheProvider.clearGroupKeys(cacheGroup);
logger.debug("method[{}] remove cache Group:{}",msId,cacheGroup);
//关联缓存
if(cacheEvictCascades.containsKey(msId)){
for (String entity : cacheEvictCascades.get(msId)) {
cacheGroup = entity + GROUPKEY_SUFFIX;
cacheProvider.clearExpiredGroupKeys(entity + GROUPKEY_SUFFIX);
logger.debug("method[{}] remove Cascade cache Group:[{}]",msId,cacheGroup);
}
}
}
/**
* 缓存其他唯一结果查询方法和主键缓存的引用
* @param object
* @param mt
* @param cacheKey
*/
private void cacheUniqueSelectRef(Object object, MappedStatement mt, String cacheKey) {
Collection mcs = queryCacheMethods.get(mt.getId().substring(0, mt.getId().lastIndexOf(SPLIT_PONIT))).values();
outter:for (QueryMethodCache methodCache : mcs) {
if(methodCache.isPk || !methodCache.uniqueResult)continue;
try {
Object[] cacheFieldValues = new Object[methodCache.fieldNames.length];
for (int i = 0; i < cacheFieldValues.length; i++) {
if(methodCache.fieldNames[i] == null)continue outter;
cacheFieldValues[i] = ReflectUtils.getObjectValue(object, methodCache.fieldNames[i]);
if(cacheFieldValues[i] == null)continue outter;
}
String fieldCacheKey = genarateQueryCacheKey(methodCache.keyPattern , cacheFieldValues);
cacheProvider.set(fieldCacheKey,cacheKey, methodCache.expire);
if(logger.isDebugEnabled())logger.debug("method[{}] add ref cacheKey:{}",mt.getId(),fieldCacheKey);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 生成查询缓存key
* @param cacheInfo
* @param param
* @return
*/
@SuppressWarnings("unchecked")
private String genarateQueryCacheKey(String keyPattern,Object param){
if(param instanceof Map){
Map map = (Map) param;
//DeviceEntity.deviceId:%s.userId:%s
Object[] args = new String[map.size()];
String[] parts = keyPattern.split(REGEX_POINT);
for (int i = 1; i < parts.length; i++) {
args[i-1] = map.get(parts[i].replace(REGEX_PLACEHOLDER, "")).toString();
}
return String.format(keyPattern, args);
}else if(param instanceof BaseEntity){
return String.format(keyPattern, ((BaseEntity)param).getId());
}else if(param instanceof Object[]){
return String.format(keyPattern, (Object[])param);
}else{
return param == null ? keyPattern : String.format(keyPattern, param.toString());
}
}
@Override
public InterceptorType getInterceptorType() {
return InterceptorType.around;
}
private QueryMethodCache getQueryMethodCache(String mtId){
String key1 = mtId.substring(0, mtId.lastIndexOf(SPLIT_PONIT));
if(queryCacheMethods.containsKey(key1)){
return queryCacheMethods.get(key1).get(mtId);
}
return null;
}
private QueryMethodCache getQueryByPkMethodCache(String mtId){
mtId = mtId.substring(0, mtId.lastIndexOf(SPLIT_PONIT));
if(queryCacheMethods.containsKey(mtId)){
return queryCacheMethods.get(mtId).get(mtId + "." + methodDefine.selectName());
}
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
if("mapper3".equalsIgnoreCase(crudDriver)){
methodDefine = new Mapper3CacheMethodDefine();
}else{
methodDefine = new DefaultCacheMethodDefine();
}
if(cacheProvider == null)cacheProvider = new DefaultCacheProvider();
List entityInfos = MybatisMapperParser.getEntityInfos();
QueryMethodCache methodCache = null;
for (EntityInfo ei : entityInfos) {
Class> mapperClass = ei.getMapperClass();
//按主键查询方法定义
QueryMethodCache queryByPKMethod = generateQueryByPKMethod(mapperClass, ei.getEntityClass());
if(queryByPKMethod == null)continue;
boolean entityWithAnnotation = ei.getEntityClass().isAnnotationPresent(Cache.class);
Cache annotationCache = null;
String keyPatternForPK = queryByPKMethod.keyPattern;
Map tmpMap = new HashMap<>();
//接口定义的自动缓存方法
Method[] methods = mapperClass.getDeclaredMethods();
for (Method method : methods) {
String fullMethodName = mapperClass.getName() + SPLIT_PONIT + method.getName();
if(method.isAnnotationPresent(Cache.class)){
annotationCache = method.getAnnotation(Cache.class);
if(tmpMap.containsKey(fullMethodName))continue;
methodCache = generateQueryMethodCacheByMethod(mapperClass, ei.getEntityClass(), method);
methodCache.expire = annotationCache.expire();
tmpMap.put(fullMethodName, methodCache);
logger.info("解析查询方法{}自动缓存配置 ok,keyPattern:[{}]",methodCache.methodName,methodCache.keyPattern);
}else if(method.isAnnotationPresent(CacheEvictCascade.class)){
CacheEvictCascade cascade = method.getAnnotation(CacheEvictCascade.class);
if(cascade.cascadeEntities().length > 0){
List entityNames = new ArrayList<>();
for (Class> clazz : cascade.cascadeEntities()) {
entityNames.add(clazz.getSimpleName());
}
cacheEvictCascades.put(fullMethodName, entityNames);
logger.info("解析查询方法{}自动关联更新缓存配置 ok,cascadeEntities:[{}]",fullMethodName,entityNames);
}
}
}
//无任何需要自动缓存的方法
if(entityWithAnnotation == false && tmpMap.isEmpty()){
continue;
}
//selectAll
QueryMethodCache selectAllMethod = generateSelectAllMethod(mapperClass, ei.getEntityClass());
tmpMap.put(selectAllMethod.methodName, selectAllMethod);
//
if(entityWithAnnotation){
queryByPKMethod.expire = ei.getEntityClass().getAnnotation(Cache.class).expire();
selectAllMethod.expire = ei.getEntityClass().getAnnotation(Cache.class).expire();
}
//缓存需要自动缓存的mapper
cacheEnableMappers.add(ei.getMapperClass().getName());
//
mapperNameRalateEntityNames.put(ei.getMapperClass().getName(), ei.getEntityClass().getSimpleName());
//主键查询方法
tmpMap.put(queryByPKMethod.methodName, queryByPKMethod);
logger.info("解析查询方法{}自动缓存配置 ok,keyPattern:[{}]",queryByPKMethod.methodName,queryByPKMethod.keyPattern);
queryCacheMethods.put(mapperClass.getName(), tmpMap);
//更新缓存方法
generateUpdateByPkCacheMethod(mapperClass, ei.getEntityClass(), keyPatternForPK);
}
//
registerClearExpiredGroupKeyTask();
}
private void registerClearExpiredGroupKeyTask(){
clearExpiredGroupKeysTimer = Executors.newScheduledThreadPool(1);
clearExpiredGroupKeysTimer.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
for (String key : groupKeys) {
try {
cacheProvider.clearExpiredGroupKeys(key);
} catch (Exception e) {
logger.warn("clearExpiredGroupKeys for {} error!!",key);
}
}
}
}, 5, 60, TimeUnit.MINUTES);
}
/**
* 生成按主键查询缓存定义
* @param mapperClass
* @param entityClass
* @return
*/
private QueryMethodCache generateQueryByPKMethod(Class> mapperClass,Class> entityClass){
QueryMethodCache methodCache = null;
Field[] fields = entityClass.getDeclaredFields();
//主键key前缀
for (Field field : fields) {
if(field.isAnnotationPresent(Id.class)){
methodCache = new QueryMethodCache();
methodCache.isPk = true;
methodCache.uniqueResult = true;
methodCache.keyPattern = entityClass.getSimpleName() + ".id:%s";
methodCache.methodName = mapperClass.getName() + "." + methodDefine.selectName();
methodCache.cacheGroupKey = entityClass.getSimpleName() + GROUPKEY_SUFFIX;
//
groupKeys.add(methodCache.cacheGroupKey);
}
}
return methodCache;
}
private QueryMethodCache generateSelectAllMethod(Class> mapperClass,Class> entityClass){
QueryMethodCache methodCache = new QueryMethodCache();
methodCache.cacheGroupKey = entityClass.getSimpleName() + GROUPKEY_SUFFIX;
methodCache.methodName = mapperClass.getName() + "." + methodDefine.selectAllName();
methodCache.keyPattern = entityClass.getSimpleName() + ".all";
methodCache.isPk = false;
methodCache.uniqueResult = false;
return methodCache;
}
private void generateUpdateByPkCacheMethod(Class> mapperClass,Class> entityClass,String keyPatternForPK){
String methodName = null;
//按主键插入
String[] insertNames = methodDefine.insertName().split(",");
for (String name : insertNames) {
methodName = mapperClass.getName() + "." + name;
updateCacheMethods.put(methodName, new UpdateByPkMethodCache(entityClass,methodName, keyPatternForPK, SqlCommandType.INSERT));
}
//按主键更新
String[] updateNames = methodDefine.updateName().split(",");
for (String name : updateNames) {
methodName = mapperClass.getName() + "." + name;
updateCacheMethods.put(methodName, new UpdateByPkMethodCache(entityClass,methodName, keyPatternForPK, SqlCommandType.UPDATE));
}
//按主键删除
methodName = mapperClass.getName() + "." + methodDefine.deleteName();
updateCacheMethods.put(methodName, new UpdateByPkMethodCache(entityClass,methodName, keyPatternForPK, SqlCommandType.DELETE));
}
/**
* 按查询方法生成缓存key前缀
* @param entityClassName
* @param method
* @return
*/
private QueryMethodCache generateQueryMethodCacheByMethod(Class> mapperClass,Class> entityClass,Method method){
QueryMethodCache methodCache = new QueryMethodCache();
String methodName = mapperClass.getName() + SPLIT_PONIT + method.getName();
methodCache.methodName = methodName;
methodCache.fieldNames = new String[method.getParameterTypes().length];
methodCache.cacheGroupKey = entityClass.getSimpleName() + GROUPKEY_SUFFIX;
methodCache.uniqueResult = method.getReturnType() != List.class && method.getReturnType() != Set.class;
StringBuilder sb = new StringBuilder(entityClass.getSimpleName()).append(SPLIT_PONIT).append(method.getName());
Annotation[][] annotations = method.getParameterAnnotations();
for (int i = 0; i < annotations.length; i++) {
Annotation[] aa = annotations[i];
if(aa.length > 0){
String fieldName = null;
inner:for (Annotation annotation : aa) {
if(annotation.toString().contains(Param.class.getName())){
fieldName = ((Param)annotation).value();
break inner;
}
}
methodCache.fieldNames[i] = fieldName;
}
//
sb.append(i == 0 ? ":" : "_").append("%s");
}
methodCache.keyPattern = sb.toString();
return methodCache;
}
/**
* 查询缓存方法
*/
private class QueryMethodCache{
public String cacheGroupKey;//缓存组key
public String methodName;
public String keyPattern;
public long expire = CacheExpires.IN_1WEEK;//过期时间(秒)
public boolean isPk = false;//主键查询
public boolean uniqueResult = true;//查询结果是否唯一记录
public String[] fieldNames;//作为查询条件的字段名称
public QueryMethodCache() {}
}
/**
* 按主键更新(add,update,delete)的方法
*/
private class UpdateByPkMethodCache{
public String keyPattern;
public SqlCommandType sqlCommandType;
public UpdateByPkMethodCache(Class> entityClass,String methodName, String keyPattern, SqlCommandType sqlCommandType) {
this.keyPattern = keyPattern;
this.sqlCommandType = sqlCommandType;
}
}
private void addCurrentThreadCacheKey(String key){
List keys = TransactionWriteCacheKeys.get();
if(keys == null){
keys = new ArrayList<>();
TransactionWriteCacheKeys.set(keys);
}
keys.add(key);
}
/**
* 回滚当前事务写入的缓存
*/
public static void rollbackCache(){
List keys = TransactionWriteCacheKeys.get();
if(keys == null)return;
for (String key : keys) {
cacheProvider.remove(key);
}
}
@Override
public void destroy() throws Exception {
cacheProvider.close();
clearExpiredGroupKeysTimer.shutdown();
}
}