All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jeesuite.mybatis.plugin.cache.CacheHandler Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package com.jeesuite.mybatis.plugin.cache;

import java.io.Serializable;
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 javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jeesuite.common.async.RetryAsyncTaskExecutor;
import com.jeesuite.common.async.RetryTask;
import com.jeesuite.common.json.JsonUtils;
import com.jeesuite.common.util.DigestUtils;
import com.jeesuite.common.util.ReflectUtils;
import com.jeesuite.mybatis.MybatisConfigs;
import com.jeesuite.mybatis.MybatisRuntimeContext;
import com.jeesuite.mybatis.core.BaseEntity;
import com.jeesuite.mybatis.core.InterceptorHandler;
import com.jeesuite.mybatis.crud.CrudMethods;
import com.jeesuite.mybatis.exception.MybatisHanlerInitException;
import com.jeesuite.mybatis.kit.CacheKeyUtils;
import com.jeesuite.mybatis.kit.MybatisSqlRewriteUtils.SqlMetadata;
import com.jeesuite.mybatis.parser.EntityInfo;
import com.jeesuite.mybatis.parser.EntityInfo.MapperMethod;
import com.jeesuite.mybatis.parser.MybatisMapperParser;
import com.jeesuite.mybatis.plugin.InvocationVals;
import com.jeesuite.mybatis.plugin.JeesuiteMybatisInterceptor;
import com.jeesuite.mybatis.plugin.cache.annotation.Cache;
import com.jeesuite.mybatis.plugin.cache.annotation.CacheIgnore;
import com.jeesuite.mybatis.plugin.cache.provider.DefaultCacheProvider;
import com.jeesuite.spring.InstanceFactory;


/**
 * 自动缓存拦截处理
 * @description 
* @author vakin * @date 2015年12月7日 * @Copyright (c) 2015, jwww */ public class CacheHandler implements InterceptorHandler { protected static final Logger logger = LoggerFactory.getLogger("com.jeesuite.mybatis.plugin.cache"); public static final String CURRENT_USER_CONTEXT_NAME = "currentUser"; private static final String BLOCK_ON_CONCURRENT_LOCK_RETURN = "_block_on_concurrentLock"; public static final String NAME = "cache"; public final static long IN_1MINS = 60; public final static long IN_1HOUR = 60 * 60; public static long defaultCacheExpire = 0; private static final String STR_PARAM = "param"; public static final String GROUPKEY_SUFFIX = "~keys"; private boolean dynamicCacheTime = false; private boolean nullValueCache = false; //null缓存占位符(避免频繁查询不存在对象造成缓存穿透导致频繁查询db) public static final String NULL_PLACEHOLDER = "~null"; private static List groupKeys = new ArrayList<>(); //需要缓存的所有mapper private static List cacheEnableMappers = new ArrayList<>(); private static Map> queryCacheMethods = new HashMap<>(); private static Map updatePkCacheMethods = new HashMap<>(); //<更新方法msId,[关联查询方法列表]> private static Map> customUpdateCacheMapppings = new HashMap<>(); protected static CacheProvider cacheProvider; public void setCacheProvider(CacheProvider cacheProvider) { CacheHandler.cacheProvider = cacheProvider; } private static CacheProvider getCacheProvider() { if(cacheProvider == null){ synchronized (CacheHandler.class) { if(cacheProvider != null)return cacheProvider; if(cacheProvider == null){ cacheProvider = InstanceFactory.getInstance(CacheProvider.class); } if(cacheProvider == null){ cacheProvider = new DefaultCacheProvider(); } logger.info("Initializing cacheProvider use:{} ",cacheProvider.getClass().getName()); } } return cacheProvider; } @Override public void start(JeesuiteMybatisInterceptor context) { nullValueCache = MybatisConfigs.getBoolean(context.getGroupName(), MybatisConfigs.CACHE_NULL_VALUE, false); dynamicCacheTime = MybatisConfigs.getBoolean(context.getGroupName(), MybatisConfigs.CACHE_DYNAMIC_EXPIRE, false); defaultCacheExpire = Long.parseLong(MybatisConfigs.getProperty(context.getGroupName(), MybatisConfigs.CACHE_EXPIRE_SECONDS, "0")); logger.info("nullValueCache:{},defaultCacheExpireSeconds:{},dynamicCacheTime:{}",nullValueCache,defaultCacheExpire,dynamicCacheTime); List entityInfos = MybatisMapperParser.getEntityInfos(context.getGroupName()); Class baseEntityClass = BaseEntity.class; QueryCacheMethodMetadata methodCache = null; for (EntityInfo ei : entityInfos) { if(ei.getMapperClass().isAnnotationPresent(CacheIgnore.class))continue; if(!baseEntityClass.isAssignableFrom(ei.getEntityClass())){ logger.warn("[{}] not extends from [{}],ignore register auto cache!!!!",ei.getEntityClass().getName(),baseEntityClass.getName()); continue; } Class mapperClass = ei.getMapperClass(); //按主键查询方法定义 QueryCacheMethodMetadata queryByPKMethod = generateQueryByPKMethod(mapperClass, ei.getEntityClass()); if(queryByPKMethod == null)continue; Map tmpMap = new HashMap<>(); //主键查询方法 tmpMap.put(queryByPKMethod.methodName, queryByPKMethod); String keyPatternForPK = queryByPKMethod.keyPattern; //接口定义的自动缓存方法 for (MapperMethod method : ei.getMapperMethods().values()) { if(method.getMethod().isAnnotationPresent(Cache.class)){ if(tmpMap.containsKey(method.getFullName()))continue; methodCache = generateQueryMethodCacheByMethod(ei, method); tmpMap.put(method.getFullName(), methodCache); logger.info("解析查询方法{}自动缓存配置 ok,keyPattern:[{}]",methodCache.methodName,methodCache.keyPattern); } } //缓存需要自动缓存的mapper cacheEnableMappers.add(ei.getMapperClass().getName()); logger.info("解析查询方法{}自动缓存配置 ok,keyPattern:[{}]",queryByPKMethod.methodName,queryByPKMethod.keyPattern); queryCacheMethods.put(mapperClass.getName(), tmpMap); //更新缓存方法 generateUpdateByPkCacheMethod(mapperClass, ei.getEntityClass(), keyPatternForPK); } // logger.info(">>>customUpdateCacheMapppings:{}",customUpdateCacheMapppings); } @Override public Object onInterceptor(InvocationVals invocationVal) throws Throwable { MappedStatement mt = invocationVal.getMappedStatement(); boolean getLock = false; String cacheKey = null; if(mt.getSqlCommandType().equals(SqlCommandType.SELECT)){ //事务方法内部的查询不走缓存 if(MybatisRuntimeContext.isTransactionalOn()){ if(logger.isDebugEnabled())logger.debug(">>auto_cache_process skipCache[isTransactionalOn] -> mapperId:{}",mt.getId()); return null; } //按主键查询 QueryCacheMethodMetadata metadata = getQueryMethodCache(mt.getId()); if(metadata == null) { return null; } cacheKey = genarateQueryCacheKey(metadata.keyPattern, invocationVal.getParameter()); invocationVal.setQueryCacheMetadata(metadata,cacheKey); //并发控制防止缓存穿透 if(!metadata.concurrency){ String concurrentLockKey = "concurrent:" + cacheKey; invocationVal.setConcurrentLockKey(concurrentLockKey); getLock = getCacheProvider().setnx(concurrentLockKey, "1", 30); if(!getLock){ if(logger.isDebugEnabled())logger.debug(">>auto_cache_process notGetConcurrentLock -> mapperId:{}",mt.getId()); return BLOCK_ON_CONCURRENT_LOCK_RETURN; } if(logger.isDebugEnabled())logger.debug(">>auto_cache_process getConcurrentLock -> mapperId:{}",mt.getId()); } Object cacheObject = null; boolean nullPlaceholder = false; // if(!metadata.isSecondQueryById()){ //从缓存读取 cacheObject = getCacheProvider().get(cacheKey); nullPlaceholder = nullValueCache && NULL_PLACEHOLDER.equals(cacheObject); if(StringUtils.isNotBlank(metadata.refKey) && (nullPlaceholder || cacheObject == null)){ cacheObject = getCacheProvider().get(metadata.refKey); nullPlaceholder = nullValueCache && NULL_PLACEHOLDER.equals(cacheObject); } if(nullPlaceholder){ if(logger.isDebugEnabled())logger.debug(">>auto_cache_process hitCache -> mapperId:{},cacheKey:{}",mt.getId(),cacheKey); }else if(cacheObject != null){ if(logger.isDebugEnabled())logger.debug(">>auto_cache_process hitCache -> mapperId:{},cacheKey:{}",mt.getId(),cacheKey); } }else{ //新根据缓存KEY找到与按ID缓存的KEY String refCacheKey = nullValueCache ? getCacheProvider().get(cacheKey) : getCacheProvider().getStr(cacheKey); if(refCacheKey != null){ if(nullPlaceholder = (nullValueCache && NULL_PLACEHOLDER.equals(refCacheKey))){ cacheObject = NULL_PLACEHOLDER; }else{ cacheObject = getCacheProvider().get(refCacheKey); if(cacheObject != null && logger.isDebugEnabled())logger.debug(">>auto_cache_process hitRefCache -> mapperId:{},cacheKey:{},refCacheKey:{}",mt.getId(),cacheKey,refCacheKey); } } } if(nullPlaceholder){ cacheObject = new ArrayList<>(0); }else if(cacheObject != null && !(cacheObject instanceof Collection)){ cacheObject = Arrays.asList(cacheObject); } return cacheObject; } return null; } @SuppressWarnings("rawtypes") @Override public void onFinished(InvocationVals invocationVal,Object result) { try { if(BLOCK_ON_CONCURRENT_LOCK_RETURN.equals(result))return; MappedStatement mt = invocationVal.getMappedStatement(); QueryCacheMethodMetadata metadata = null; if(mt.getSqlCommandType().equals(SqlCommandType.SELECT)){ if(result == null)return; if((metadata = invocationVal.getQueryMethodMetadata()) == null)return; final String cacheKey = invocationVal.getCacheKey(); if(result instanceof List){ List list = (List)result; if(list.isEmpty()){ if(nullValueCache){ getCacheProvider().set(cacheKey,NULL_PLACEHOLDER, IN_1MINS); } return; } result = metadata.collectionResult ? result : list.get(0); } // if(!metadata.isSecondQueryById()){ if(getCacheProvider().set(cacheKey,result, metadata.getExpire())){ if(logger.isDebugEnabled())logger.debug(">>auto_cache_process addCache -> mapperId:{},cacheKey:{}",mt.getId(),cacheKey); } if(metadata.isPk){//唯一索引(业务上) cacheUniqueSelectRef(result, mt, cacheKey); }else if(metadata.groupRalated){//结果为集合的情况,增加key到cacheGroup getCacheProvider().putGroup(metadata.cacheGroupKey, cacheKey); } }else{ //之前没有按主键的缓存,增加按主键缓存 String idCacheKey = genarateQueryCacheKey(getQueryByPkMethodCache(mt.getId()).keyPattern,result); if(idCacheKey != null && cacheKey != null){ if(!getCacheProvider().exists(idCacheKey)){ //缓存idkey->实体 getCacheProvider().set(idCacheKey,result, metadata.getExpire()); } //缓存fieldkey->idkey cacheFieldRefKey(cacheKey,idCacheKey, metadata.getExpire()); if(logger.isDebugEnabled())logger.debug(">>auto_cache_process addCache -> mapperId:{},idCacheKey:{},cacheKey:{}",mt.getId(),idCacheKey,cacheKey); } } }else{ String mapperClassName = invocationVal.getMapperNameSpace(); if(!cacheEnableMappers.contains(mapperClassName) && !customUpdateCacheMapppings.containsKey(mt.getId()))return; //返回0,未更新成功 if(result != null && ((int)result) == 0)return; //更新方法移除缓存,避免事务回滚导致缓存不一致,所以更新方法直接移除缓存 if(updatePkCacheMethods.containsKey(mt.getId()) && !mt.getSqlCommandType().equals(SqlCommandType.INSERT)){ UpdateByPkCacheMethodMetadata updateMethodCache = updatePkCacheMethods.get(mt.getId()); String idCacheKey = genarateQueryCacheKey(updateMethodCache.keyPattern,invocationVal.getParameter()); getCacheProvider().remove(idCacheKey); //针对按条件更新或者删除的方法,按查询条件查询相关内容,然后清理对应主键缓存内容 // EntityInfo entityInfo = MybatisMapperParser.getEntityInfoByMapper(mapperClassName); // final Object parameter = args[1]; // BoundSql boundSql = mt.getBoundSql(parameter); // String orignSql = StringUtils.replace(boundSql.getSql(), ";$", StringUtils.EMPTY); // SqlMetadata sqlMetadata = MybatisSqlRewriteUtils.rewriteAsSelectPkField(orignSql, entityInfo.getIdColumn()); // // if(entityInfo.getTableName().equalsIgnoreCase(sqlMetadata.getTableName())) { // removeCacheByDyncQuery(sqlMetadata,parameter); // } } //删除同一cachegroup关联缓存 removeCacheByGroup(mt.getId(), mapperClassName); //删除自定义关联缓存 if(customUpdateCacheMapppings.containsKey(mt.getId())) { removeCustomRelateCache(mt.getId()); } } } finally { //清除并发控制锁 if(invocationVal.getConcurrentLockKey() != null){ cacheProvider.remove(invocationVal.getConcurrentLockKey()); } } } /** * 缓存其他唯一结果查询方法和主键缓存的引用 * @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(InvocationVals.DOT))).values(); outter:for (QueryCacheMethodMetadata methodCache : mcs) { if(!methodCache.isSecondQueryById())continue; try { Object[] cacheFieldValues = new Object[methodCache.fieldNames.length]; for (int i = 0; i < cacheFieldValues.length; i++) { if(methodCache.fieldNames[i] == null)break outter; cacheFieldValues[i] = ReflectUtils.getObjectValue(object, methodCache.fieldNames[i]); if(cacheFieldValues[i] == null)continue outter; } String fieldCacheKey = genarateQueryCacheKey(methodCache.keyPattern , cacheFieldValues); cacheFieldRefKey(fieldCacheKey,cacheKey, methodCache.getExpire()); if(logger.isDebugEnabled())logger.debug(">>auto_cache_process addRefCache -> mapperId:{},cacheKey:{},refkey:{}",mt.getId(),fieldCacheKey,cacheKey); } catch (Exception e) { logger.warn("cacheUniqueSelectRef:"+cacheKey,e); } } } /** * 缓存字段查询到idkey * @param fieldCacheKey * @param idCacheKey * @param expired */ private void cacheFieldRefKey(String fieldCacheKey,String idCacheKey,long expired){ if(nullValueCache){ getCacheProvider().set(fieldCacheKey, idCacheKey, expired); }else{ getCacheProvider().setStr(fieldCacheKey, idCacheKey, expired); } } /** * 根据动态查询内容清理缓存 * @param sqlMetadata 查询主键列表SQL语句信息 * @param parameter 参数 */ @SuppressWarnings({ "unchecked" }) private void removeCacheByDyncQuery(SqlMetadata sqlMetadata, Object parameter) { if(parameter instanceof BaseEntity) { //TODO //sqlSessionFactory.openSession().selectList("findByExample") } List parameterList = new ArrayList<>(sqlMetadata.getParameterNums()); if(parameter instanceof Map){ Map map = (Map) parameter; Object value; for (int i = sqlMetadata.getWhereParameterIndex(); i < 100; i++) { value = map.get(STR_PARAM + i); if(value == null){ break; } valueToParamList(parameterList, value); } }else { valueToParamList(parameterList, parameter); } if(parameterList.size() != sqlMetadata.getParameterNums()) { logger.warn(">>parameterNotMatch -> sql:{},parameterList:{}",sqlMetadata.getSql()); return; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private void valueToParamList(List result,Object value) { if(value instanceof Map) { Collection values = ((Map)value).values(); for (Object obj : values) { valueToParamList(result, obj); } }else if(value instanceof Collection) { result.addAll((Collection) value); }else { result.add(value); } } /** * 删除缓存组 * @param msId * @param mapperClassName * @param removePkCache 是否同时删除按主键的缓存 */ private void removeCacheByGroup(String msId, String mapperClassName) { EntityInfo entityInfo = MybatisMapperParser.getEntityInfoByMapper(mapperClassName); if(entityInfo == null)return; final String groupName = entityInfo.getEntityClass().getSimpleName(); RetryAsyncTaskExecutor.execute(new RetryTask() { @Override public String traceId() { return msId; } @Override public boolean process() throws Exception { getCacheProvider().clearGroup(groupName); if(logger.isDebugEnabled())logger.debug(">>auto_cache_process removeGroupCache -> mapperId:{},groupName:{}",msId,groupName); return true; } }); } /** * 删除更新方法自定义缓存关系 * @param updateId */ private void removeCustomRelateCache(String updateId) { final List queryMethods = customUpdateCacheMapppings.get(updateId); RetryAsyncTaskExecutor.execute(new RetryTask() { @Override public String traceId() { return updateId; } @Override public boolean process() throws Exception { QueryCacheMethodMetadata metadata; for (String method : queryMethods) { metadata = getQueryMethodCache(method); String prefix = StringUtils.splitByWholeSeparator(metadata.keyPattern, "%s")[0]; cacheProvider.clearGroup(metadata.cacheGroupKey,prefix); } return true; } }); } /** * 生成查询缓存key * @param cacheInfo * @param param * @return */ @SuppressWarnings("unchecked") public static String genarateQueryCacheKey(String keyPattern,Object param){ String text; try { Object[] args; if(param instanceof Map){ Map map = (Map) param; if(map.containsKey(STR_PARAM + "1")){ args = new String[map.size()/2]; for (int i = 0; i < args.length; i++) { args[i] = CacheKeyUtils.objcetToString(map.get(STR_PARAM + (i+1))); } }else{ args = new String[]{CacheKeyUtils.objcetToString(map)}; } }else if(param instanceof BaseEntity){ Serializable id = ((BaseEntity)param).getId(); if(id != null && !"0".equals(id.toString())){ args = new String[]{(((BaseEntity)param).getId()).toString()}; }else{ args = new String[]{CacheKeyUtils.objcetToString(param)}; } }else if(param instanceof Object[]){ args = (Object[])param; }else if(param == null){ args = new Object[0]; }else{ args = new String[]{CacheKeyUtils.objcetToString(param)}; } text = StringUtils.join(args,"-"); } catch (Exception e) { text = JsonUtils.toJson(param); e.printStackTrace(); } if(text.length() > 64)text = DigestUtils.md5(text); return String.format(keyPattern, text); } private QueryCacheMethodMetadata getQueryMethodCache(String mtId){ String key1 = mtId.substring(0, mtId.lastIndexOf(InvocationVals.DOT)); if(queryCacheMethods.containsKey(key1)){ return queryCacheMethods.get(key1).get(mtId); } return null; } private QueryCacheMethodMetadata getQueryByPkMethodCache(String mtId){ mtId = mtId.substring(0, mtId.lastIndexOf(InvocationVals.DOT)); if(queryCacheMethods.containsKey(mtId)){ return queryCacheMethods.get(mtId).get(mtId + "." + CrudMethods.selectByPrimaryKey.name()); } return null; } /** * 生成按主键查询缓存定义 * @param mapperClass * @param entityClass * @return */ private QueryCacheMethodMetadata generateQueryByPKMethod(Class mapperClass,Class entityClass){ QueryCacheMethodMetadata methodCache = null; Field[] fields = entityClass.getDeclaredFields(); //主键key前缀 for (Field field : fields) { if(field.isAnnotationPresent(Id.class)){ methodCache = new QueryCacheMethodMetadata(); methodCache.isPk = true; methodCache.collectionResult = false; methodCache.keyPattern = entityClass.getSimpleName() + ".id:%s"; methodCache.methodName = mapperClass.getName() + "." + CrudMethods.selectByPrimaryKey.name(); methodCache.expire = defaultCacheExpire; methodCache.cacheGroupKey = entityClass.getSimpleName() + GROUPKEY_SUFFIX; break; } } groupKeys.add(methodCache.cacheGroupKey); return methodCache; } private void generateUpdateByPkCacheMethod(Class mapperClass,Class entityClass,String keyPatternForPK){ String methodName = null; methodName = mapperClass.getName() + "." + CrudMethods.insert.name(); updatePkCacheMethods.put(methodName, new UpdateByPkCacheMethodMetadata(entityClass,methodName, keyPatternForPK, SqlCommandType.INSERT)); methodName = mapperClass.getName() + "." + CrudMethods.insertSelective.name(); updatePkCacheMethods.put(methodName, new UpdateByPkCacheMethodMetadata(entityClass,methodName, keyPatternForPK, SqlCommandType.INSERT)); // methodName = mapperClass.getName() + "." + CrudMethods.updateByPrimaryKey.name(); updatePkCacheMethods.put(methodName, new UpdateByPkCacheMethodMetadata(entityClass,methodName, keyPatternForPK, SqlCommandType.UPDATE)); methodName = mapperClass.getName() + "." + CrudMethods.updateByPrimaryKeySelective.name(); updatePkCacheMethods.put(methodName, new UpdateByPkCacheMethodMetadata(entityClass,methodName, keyPatternForPK, SqlCommandType.UPDATE)); //按主键删除 methodName = mapperClass.getName() + "." + CrudMethods.deleteByPrimaryKey.name(); updatePkCacheMethods.put(methodName, new UpdateByPkCacheMethodMetadata(entityClass,methodName, keyPatternForPK, SqlCommandType.DELETE)); } /** * 按查询方法生成缓存key前缀 * @param entityClassName * @param method * @return */ private QueryCacheMethodMetadata generateQueryMethodCacheByMethod(EntityInfo entityInfo,MapperMethod mapperMethod){ Method method = mapperMethod.getMethod(); Cache cacheAnnotation = method.getAnnotation(Cache.class); String[] evictOnMethods = cacheAnnotation.evictOnMethods(); Class mapperClass = entityInfo.getMapperClass(); Class entityClass = entityInfo.getEntityClass(); QueryCacheMethodMetadata methodCache = new QueryCacheMethodMetadata(); methodCache.methodName = mapperClass.getName() + InvocationVals.DOT + method.getName(); methodCache.concurrency = cacheAnnotation.concurrency(); methodCache.uniqueIndex = cacheAnnotation.uniqueIndex(); methodCache.cacheGroupKey = entityClass.getSimpleName() + GROUPKEY_SUFFIX; if(cacheAnnotation.userScope()){ methodCache.contextParam = CURRENT_USER_CONTEXT_NAME; }else if(cacheAnnotation.scopeContext().length > 0){ methodCache.contextParam = cacheAnnotation.scopeContext()[0]; } if(cacheAnnotation.refKey().length > 0){ methodCache.refKey = cacheAnnotation.refKey()[0]; } if(methodCache.contextParam != null && evictOnMethods.length == 0){ evictOnMethods = new String[]{"*"}; } methodCache.checkExpired = evictOnMethods.length > 0; if(cacheAnnotation.expire() > 0){ methodCache.expire = cacheAnnotation.expire(); }else if(cacheAnnotation.userScope()){ methodCache.expire = IN_1MINS * 10 < defaultCacheExpire ? IN_1MINS * 10 : defaultCacheExpire; } if(methodCache.uniqueIndex && method.getReturnType() != entityClass){ throw new MybatisHanlerInitException("@Cache with[uniqueIndex = true] but ReturnType not Match ["+entityClass.getName()+"]"); } methodCache.collectionResult = method.getReturnType() == List.class || method.getReturnType() == Set.class; if(methodCache.collectionResult){ methodCache.groupRalated = true; }else{ // count等统计查询 methodCache.groupRalated = method.getReturnType().isAnnotationPresent(Table.class) == false; } methodCache.fieldNames = new String[method.getParameterTypes().length]; Annotation[][] annotations = method.getParameterAnnotations(); boolean uniqueQuery = method.getReturnType().isAnnotationPresent(Table.class); 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; } } if(uniqueQuery && MybatisMapperParser.entityHasProperty(entityClass, fieldName)){ methodCache.fieldNames[i] = fieldName; } } // } methodCache.keyPattern = new StringBuilder(entityClass.getSimpleName()).append(InvocationVals.DOT).append(method.getName()).append(":%s").toString(); if(uniqueQuery){ for (String name : methodCache.fieldNames) { if(StringUtils.isBlank(name)){ methodCache.fieldNames = null; break; } } } // buildEvictOnMethods(mapperClass.getName(),mapperMethod,evictOnMethods); return methodCache; } /** * 构建自定义缓存更新关系 * @param mapperClassName * @param method * @param evictOnMethods */ private void buildEvictOnMethods(String mapperClassName,MapperMethod method,String[] evictOnMethods) { if(evictOnMethods == null|| evictOnMethods.length == 0){ return; } String targetMethodFullNamePrefix = mapperClassName.substring(0, mapperClassName.lastIndexOf(".") + 1); String targetMapperClassName = null; for (String methodName : evictOnMethods) { if("*".equals(methodName)){ methodName = mapperClassName + ".*"; }else if(!methodName.contains(InvocationVals.DOT)) { methodName = mapperClassName + InvocationVals.DOT + methodName; } if(!methodName.startsWith(targetMethodFullNamePrefix)){ methodName = targetMethodFullNamePrefix + methodName; } targetMapperClassName = methodName.substring(0,methodName.lastIndexOf(".")); if(!methodName.endsWith("*")){ addCacheCheckRelations(methodName, method.getFullName()); }else{ EntityInfo methodEntityInfo = MybatisMapperParser.getEntityInfoByMapper(targetMapperClassName); if(methodEntityInfo == null){ continue; } for (MapperMethod mm : methodEntityInfo.getMapperMethods().values()) { if(mm.getSqlType() == SqlCommandType.SELECT)continue; if(mm.getFullName().contains(methodName.replace("*", ""))){ addCacheCheckRelations(mm.getFullName(), method.getFullName()); } } } } } private void addCacheCheckRelations(String updateMethodName,String queryMethodName){ List list = customUpdateCacheMapppings.get(updateMethodName); if(list == null){ list = new ArrayList<>(); customUpdateCacheMapppings.put(updateMethodName, list); } list.add(queryMethodName); } @Override public void close() { try { getCacheProvider().close(); } catch (Exception e) {} } @Override public int interceptorOrder() { return 4; } }