本文以mybatis-3.5.11版本為基礎,對mybatis快取進行較詳細的決議,
注意,本文說明的情況,適用于mybatis單獨使用的情況,即,不與spring或其他容器框架結合使用的情況,
快取概念說明
mybatis官方檔案中,沒有對快取的明確定義,但參考到了兩個快取概念:
在官方檔案中翻找一遍,只有兩個概念:本地快取,二級快取,并沒有一級快取的概念,本文沿用官方檔案說明,使用本地快取與二級快取的概念,
本地快取
mybatis中有SqlSession介面,所有資料庫操作,均由該介面完成,使用mybatis時,也需要通過該介面進行資料庫操作,
本地快取,由BaseExecutor的localCache實作,并且是在構造方法中進行初始化,因此,本地快取,是mybatis所有執行器都具備的快取功能,
1 protected BaseExecutor(Configuration configuration, Transaction transaction) { 2 ... 3 this.localCache = new PerpetualCache("LocalCache"); // 初始化本地快取 4 ... 5 }
本地快取的清空:
- 在進行update、commit、rollback操作時,會被清空,“為什么delete的時候沒被清空呢?”,因為delete是使用update實作的;
- 在顯示設定了flushCache=true的時候,執行陳述句后清空快取;
- 快取生命周期設定為statement的時候(默認為session),執行完陳述句會清空;
- 超時后會被清空(默認貌似是60秒);
- 顯示呼叫clearCache的時候,
統計未必全面,原始碼中呼叫路徑較多,沒有進行全面的檢查,
本地快取key的組成部分中,包含mapper方法簽名,例如,org.messi.dao.TestMapper 介面中,有一個 getUser 方法,則簽名中包含 org.messi.dao.TestMapper.getUser 串,
因此,本地快取,僅對同一mapper中的同一方法有效,
二級快取
相對于本地快取,二級快取的實作更有必要進行說明,本地快取的實作非常直白,操作簡單,二級快取功能強大了一些,實作也繞了一點,
二級快取的適用范圍,不再有同一sqlSession的局限,二級快取保存在mapperStatement中,但是使用的仍然是與一級快取相同的key,因此,二級快取跨sqlSession,但是無法跨mapper,當一個流程中使用一個sqlSession呼叫多個mapper方法時,這多個mapper的查詢陳述句,都會被快取起來,并且是在一個快取之中,下次再呼叫這個流程時(快取超時前),多個mapper的方法,都可以走快取,這是二級快取支持的重點,
下面用一張圖來表示二級快取的實作原理:

如上圖,左邊兩個mapper,表示XxxMapper.xml映射檔案,右邊的事務性快取管理器,是CachingExecutor中的一個屬性,因此二級快取,必須手動開啟,才會生效,手動開啟后,會使用CachingExecutor對指定的執行器進行裝飾,從而獲得二級快取功能,
二級快取也支持自定義的cache,可以通過自定義cache,實作自己想要的結果,例如使用redis快取,從而實作分布式部署下的快取同步,二級快取的實作原理,到此就描述完畢了,
以下是二級快取實作的截取相關代碼,可以不看,
以下是二級快取實作的截取相關代碼,可以不看,
以下是二級快取實作的截取相關代碼,可以不看,
XxxMapper.xml映射檔案的決議代碼(XmlConfigBuilder):
1 public Configuration parse() { 2 if (parsed) { 3 throw new BuilderException("Each XMLConfigBuilder can only be used once."); 4 } 5 parsed = true; 6 // 決議配置,這里是對所有配置的決議入口 7 parseConfiguration(parser.evalNode("/configuration")); 8 return configuration; 9 }
決議方法展開:
1 private void parseConfiguration(XNode root) { 2 try { 3 // issue #117 read properties first 4 propertiesElement(root.evalNode("properties")); 5 Properties settings = settingsAsProperties(root.evalNode("settings")); 6 loadCustomVfs(settings); 7 loadCustomLogImpl(settings); 8 typeAliasesElement(root.evalNode("typeAliases")); 9 pluginElement(root.evalNode("plugins")); 10 objectFactoryElement(root.evalNode("objectFactory")); 11 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 12 reflectorFactoryElement(root.evalNode("reflectorFactory")); 13 settingsElement(settings); 14 // read it after objectFactory and objectWrapperFactory issue #631 15 environmentsElement(root.evalNode("environments")); 16 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 17 typeHandlerElement(root.evalNode("typeHandlers")); 18 // 決議xml映射檔案 19 mapperElement(root.evalNode("mappers")); 20 } catch (Exception e) { 21 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 22 } 23 }
最終決議代碼(XMLMapperBuilder):
1 private void configurationElement(XNode context) { 2 try { 3 String namespace = context.getStringAttribute("namespace"); 4 if (namespace == null || namespace.isEmpty()) { 5 throw new BuilderException("Mapper's namespace cannot be empty"); 6 } 7 builderAssistant.setCurrentNamespace(namespace); 8 cacheRefElement(context.evalNode("cache-ref")); 9 cacheElement(context.evalNode("cache")); 10 parameterMapElement(context.evalNodes("/mapper/parameterMap")); 11 resultMapElements(context.evalNodes("/mapper/resultMap")); 12 sqlElement(context.evalNodes("/mapper/sql")); 13 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 14 } catch (Exception e) { 15 throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); 16 } 17 }
cache標簽決議代碼(XMLMapperBuilder):
1 private void cacheElement(XNode context) { 2 if (context != null) { 3 String type = context.getStringAttribute("type", "PERPETUAL"); 4 // 決議指定的快取型別,如果沒有指定,默認為mybatis的永久快取型別, 5 Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); 6 // 決議快取剔除策略,默認是LRU, 7 String eviction = context.getStringAttribute("eviction", "LRU"); 8 Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); 9 Long flushInterval = context.getLongAttribute("flushInterval"); 10 Integer size = context.getIntAttribute("size"); 11 boolean readWrite = !context.getBooleanAttribute("readOnly", false); 12 boolean blocking = context.getBooleanAttribute("blocking", false); 13 Properties props = context.getChildrenAsProperties(); 14 // 創建cache物件 15 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); 16 } 17 }cache標簽決議
快取物件創建代碼(MapperBuilderAssistant):
1 public Cache useNewCache(Class<? extends Cache> typeClass, 2 Class<? extends Cache> evictionClass, 3 Long flushInterval, 4 Integer size, 5 boolean readWrite, 6 boolean blocking, 7 Properties props) { 8 Cache cache = new CacheBuilder(currentNamespace) 9 .implementation(valueOrDefault(typeClass, PerpetualCache.class)) 10 .addDecorator(valueOrDefault(evictionClass, LruCache.class)) 11 .clearInterval(flushInterval) 12 .size(size) 13 .readWrite(readWrite) 14 .blocking(blocking) 15 .properties(props) 16 .build(); 17 configuration.addCache(cache); 18 // 注意這里 19 currentCache = cache; 20 return cache; 21 } public Cache useNewCache(Class<? extends Cache> typeClass, 22 Class<? extends Cache> evictionClass, 23 Long flushInterval, 24 Integer size, 25 boolean readWrite, 26 boolean blocking, 27 Properties props) { 28 Cache cache = new CacheBuilder(currentNamespace) 29 .implementation(valueOrDefault(typeClass, PerpetualCache.class)) 30 .addDecorator(valueOrDefault(evictionClass, LruCache.class)) 31 .clearInterval(flushInterval) 32 .size(size) 33 .readWrite(readWrite) 34 .blocking(blocking) 35 .properties(props) 36 .build(); 37 configuration.addCache(cache); 38 // 注意這里 39 currentCache = cache; 40 return cache; 41 }快取物件創建
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/509616.html
標籤:其他
上一篇:leetcode 208. Implement Trie (Prefix Tree) 實作 Trie (前綴樹) (中等)

