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.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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy