前面有写了一篇关于这个,但是这几天又改进了一点,就单独一篇在详细说明一下
配置 application.properties ,启用Ehcache
1 # Ehcache缓存 2 spring.cache.type=ehcache 3 spring.cache.ehcache.config=classpath:/ehcache.xml
配置 ehcache.xml ,设置缓存相关属性
1 xml version="1.0" encoding="UTF-8"?> 2 3 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:noNamespaceSchemaLocation="ehcache.xsd"> 5 6 17 <diskStore path="java.io.tmpdir" /> 18 19 20 23 <cacheManagerPeerProviderFactory 24 class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 25 properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446,timeToLive=255"/> 26 29 30 31 34 <cacheManagerPeerListenerFactory 35 class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/> 36 37 38 49 <defaultCache maxElementsInMemory="10000" eternal="false" 50 timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" /> 51 52 <cache name="*.*.*.*.dao.DeviceMapper" maxElementsInMemory="10000" eternal="false" 53 timeToIdleSeconds="120" overflowToDisk="true" /> 54 55 <cache name="*.*.*.*.dao.ProjectMapper" maxElementsInMemory="10000" eternal="false" 56 timeToIdleSeconds="120" overflowToDisk="true" /> 57 58 <cache name="*.*.*.*.dao.WarnMapper" maxElementsInMemory="10000" eternal="false" 59 timeToIdleSeconds="120" timeToLiveSeconds="300" overflowToDisk="true" /> 60 61 ehcache>
配置 cache-dependencies.xml ,指定 各namespace缓存之间的依赖关联
1 xml version="1.0" encoding="UTF-8"?> 2 <dependencies> 3 4 <relations> 5 6 <relation key="Project"> 7 <cacheNamespace>*.*.*.*.dao.ProjectMappercacheNamespace> 8 relation> 9 <relation key="Device"> 10 <cacheNamespace>*.*.*.*.dao.DeviceMappercacheNamespace> 11 relation> 12 <relation key="Warn"> 13 <cacheNamespace>*.*.*.*.dao.WarnMappercacheNamespace> 14 relation> 15 relations> 16 17 <statements> 18 23 <statement id="*.*.*.*.dao.ProjectMapper" type="uni-directional"> 24 <observer id="*.*.*.*.dao.WarnMapper" /> 25 statement> 26 <statement id="*.*.*.*.dao.DeviceMapper" type="uni-directional"> 27 <observer id="*.*.*.*.dao.WarnMapper" /> 28 statement> 29 30 35 38 39 40 statements> 41 42 dependencies>
编写常量类 CacheConstants
1 package *.*.*.*.constants; 2 3 public class CacheConstants { 4 5 /** 6 * ehcache_config 7 */ 8 // public static final String EHCACHE_CONFIG = "src/main/resources/ehcache.xml"; 9 public static final String EHCACHE_CONFIG = "ehcache.xml"; 10 11 /** 12 * cache_dependencies 13 */ 14 // public static final String CACHE_DEPENDENCIES = "src/main/resources/cache-dependencies.xml"; 15 public static final String CACHE_DEPENDENCIES = "cache-dependencies.xml"; 16 17 18 }
编写 EhcacheUtil 类
1 package *.*.*.*.utils; 2 3 import java.io.File; 4 import java.util.ArrayList; 5 import java.util.List; 6 import java.util.Map; 7 import java.util.concurrent.ConcurrentHashMap; 8 9 import org.dom4j.Document; 10 import org.dom4j.DocumentException; 11 import org.dom4j.Element; 12 import org.dom4j.io.SAXReader; 13 import org.slf4j.Logger; 14 import org.slf4j.LoggerFactory; 15 16 import *.*.*.*.constants.CacheConstants; 17 18 import net.sf.ehcache.Cache; 19 import net.sf.ehcache.CacheManager; 20 21 public class EhcacheUtil { 22 23 private static final Logger logger = LoggerFactory.getLogger(EhcacheUtil.class); 24 // 构建一个缓存管理器、单例对象 25 // private static CacheManager cacheManager = CacheManager.newInstance("src/main/resources/ehcache.xml"); 26 // private static CacheManager cacheManager = CacheManager.newInstance(CacheConstants.EHCACHE_CONFIG);
private static CacheManager cacheManager = null ; 27 // 加载缓存依赖关系的XML 28 // private static String cache_dependencies_xml_path = "src/main/resources/cache-dependencies.xml" ; 29 private static String cache_dependencies_xml_path = CacheConstants.CACHE_DEPENDENCIES ; 30 // 前面一个调用类的类名 31 private static String className = new Exception().getStackTrace()[1].getClassName() ; 32 private static String CLASS_RELATE_CACHE_MAP = "classRelateCacheMap" ; 33 private static String CACHE_RELATE_CACHE_MAP = "cacheRelateCacheMap" ; 34
static{
cacheManager= CacheManager.newInstance(EhcacheUtil.class.getClassLoader().getResourceAsStream(CacheConstants.EHCACHE_CONFIG));
}
35 /** 36 * action 根据调用类的类名,清除相关联的缓存 37 * @return 38 */ 39 @SuppressWarnings("unchecked") 40 public static void clearRelatedCache(String className) { 41 // System.out.println("className: "+className); 42 Mapmap = parseXml() ; 43 Map classRelateCacheMap = (Map ) map.get(CLASS_RELATE_CACHE_MAP) ; 44 Map > cacheRelateCacheMap = (Map >) map.get(CACHE_RELATE_CACHE_MAP) ; 45 String sourceCacheName = null ; 46 for (String key : classRelateCacheMap.keySet()) { 47 if ( className.contains(key) ) { 48 sourceCacheName = (String) classRelateCacheMap.get(key) ; 49 } 50 } 51 if ( sourceCacheName==null ) { 52 return ; 53 } 54 List destCacheNames = new ArrayList () ; 55 for (String key : cacheRelateCacheMap.keySet()) { 56 if ( key.equals(sourceCacheName) ) { 57 destCacheNames = cacheRelateCacheMap.get(key) ; 58 } 59 } 60 for (String cacheNameNeedClear : destCacheNames) { 61 clearRelatedCache(cacheNameNeedClear, null); 62 } 63 } 64 65 /** 66 * action 自动识别调用类的类名,清除相关联的缓存 67 * @return 68 */ 69 @SuppressWarnings("unchecked") 70 public static void clearRelatedCache() { 71 // System.out.println("className: "+className); 72 Map map = parseXml() ; 73 // System.out.println( map.toString() ); 74 Map classRelateCacheMap = (Map ) map.get(CLASS_RELATE_CACHE_MAP) ; 75 Map > cacheRelateCacheMap = (Map >) map.get(CACHE_RELATE_CACHE_MAP) ; 76 String sourceCacheName = null ; 77 for (String key : classRelateCacheMap.keySet()) { 78 if ( className.contains(key) ) { 79 sourceCacheName = (String) classRelateCacheMap.get(key) ; 80 // System.out.println("sourceCacheName: "+sourceCacheName); 81 } 82 } 83 if ( sourceCacheName==null ) { 84 return ; 85 } 86 List destCacheNames = new ArrayList () ; 87 for (String key : cacheRelateCacheMap.keySet()) { 88 if ( key.equals(sourceCacheName) ) { 89 destCacheNames = cacheRelateCacheMap.get(key) ; 90 } 91 } 92 for (String cacheNameNeedClear : destCacheNames) { 93 // System.out.println("cacheNameNeedClear: "+cacheNameNeedClear); 94 clearRelatedCache(cacheNameNeedClear, null); 95 } 96 } 97 98 /** 99 * action 清除相关联的缓存 100 * @param cacheName 缓存所在namespace的名称 101 * @param keys 缓存所在namespace下key的名称,为空则默认清空所有key 102 * @return 103 */ 104 public static void clearRelatedCache( String cacheName, String[] keys ) { 105 Cache cache = cacheManager.getCache(cacheName) ; 106 if ( cache==null ) { 107 return ; 108 } 109 //若缓存不为空 110 if ( keys==null || keys.length==0 ) { 111 cache.removeAll(); 112 } 113 else { 114 for (String key : keys) { 115 cache.remove(key) ; 116 } 117 } 118 // String[] cacheNames = cacheManager.getCacheNames() ; 119 // System.out.println(Arrays.asList(cacheNames)); 120 } 121 122 public static Map parseXml() { 123 Map classRelateCacheMap = new ConcurrentHashMap () ; 124 Map > cacheRelateCacheMap = new ConcurrentHashMap >() ; 125 Map map = new ConcurrentHashMap () ; 126 SAXReader saxReader = new SAXReader() ; 127 Document document = null ; 128 try { 129 // File file = new File(cache_dependencies_xml_path) ; 130 // document = saxReader.read(file);
document = saxReader.read(EhcacheUtil.class.getClassLoader().getResourceAsStream(CacheConstants.CACHE_DEPENDENCIES));
131 Element rootEl = document.getRootElement() ; 132 readElementAll(rootEl, classRelateCacheMap, cacheRelateCacheMap) ; 133 } 134 catch (DocumentException e) { 135 logger.warn(e.getMessage()) ; 136 } catch (Exception e) { 137 logger.warn(e.getMessage()) ; 138 } 139 finally { 140 map.put(CLASS_RELATE_CACHE_MAP, classRelateCacheMap) ; 141 map.put(CACHE_RELATE_CACHE_MAP, cacheRelateCacheMap) ; 142 } 143 return map ; 144 } 145 146 /** 147 * XML转Map 从根节点开始,逐层递归遍历所有子节点 148 */ 149 @SuppressWarnings("unchecked") 150 public static void readElementAll(Element node, MapclassRelateCacheMap, Map > cacheRelateCacheMap) { 151 // 当前节点的名称、文本内容和属性 152 // System.out.println("当前节点的名称:"+node.getName());//当前节点名称 153 // System.out.println("当前节点的内容:"+node.getText());//当前节点的值 154 // 所有一级子节点的list 155 List listElement = node.elements(); 156 // 逐级遍历所有子节点 157 for (Element e : listElement) { 158 if (e.getName().equals("relation")) { 159 List listCacheNamespace = e.elements() ; 160 for (Element eCacheNamespace : listCacheNamespace) { 161 classRelateCacheMap.put(e.attributeValue("key"), eCacheNamespace.getText()) ; 162 // 取到value后,要把这个节点删掉,不然在下一级会被再处理一遍 163 e.remove(eCacheNamespace); 164 } 165 } else if ( e.getName().equals("statement") ) { 166 String type = e.attributeValue("type") ; 167 List listObserver = e.elements(); 168 List list = new ArrayList () ; 169 for (Element eObserver : listObserver) { 170 list.add( eObserver.attributeValue("id") ) ; 171 if ( type.equals("bi-directional") ) { 172 List olist = cacheRelateCacheMap.get( eObserver.attributeValue("id") ) ; 173 olist.add( e.attributeValue("id") ) ; 174 cacheRelateCacheMap.put(eObserver.attributeValue("id"), olist) ; 175 } 176 e.remove(eObserver); 177 } 178 cacheRelateCacheMap.put(e.attributeValue("id"), list) ; 179 } 180 readElementAll(e, classRelateCacheMap, cacheRelateCacheMap) ; 181 } 182 } 183 184 }
在 BaseService
1 /** 2 * 基于通用MyBatis Mapper插件的Service接口的实现 3 */ 4 public abstract class BaseServiceimplements Service { 5 6 @Autowired 7 protected Mapper mapper; 8 9 private Class modelClass; // 当前泛型真实类型的Class 10 11 public BaseService() { 12 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); 13 modelClass = (Class ) pt.getActualTypeArguments()[0]; 14 } 15 16 public void save(T model) { 17 mapper.insertSelective(model); 18 EhcacheUtil.clearRelatedCache(modelClass.getName()); 19 } 20 21 public void save(List models) { 22 mapper.insertList(models); 23 EhcacheUtil.clearRelatedCache(modelClass.getName()); 24 } 25 26 public void deleteById(Integer id) { 27 mapper.deleteByPrimaryKey(id); 28 EhcacheUtil.clearRelatedCache(modelClass.getName()); 29 }
至此,解决了Mybatis二级缓存数据脏读问题
190805 - v2
共同学习,共同进步,若有补充,欢迎指出,谢谢!