日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入了解Mybatis架构设计

發布時間:2025/1/21 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入了解Mybatis架构设计 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

架構設計

我們可以把Mybatis的功能架構分為三層:

  • API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
  • Mybatis和數據庫的交互有兩種方式:

    • 使用傳統的Mybatis提供API
    • 使用Mapper代理的方式
  • 數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。他主要的目的是根據調用的請求完成一次數據庫操作。
  • 基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來最為基礎組件。為上層的數據處理層提供最基礎的支撐。
  • Mybatis主要構件

    構件描述
    SqlSession作為Mybatis工作的主要頂層API,表示和數據庫交互的會話,完成必要數據庫增刪查改功能
    ExecutorMybatis執行器,是Mybatis調度的核心,負責SQL語句的生成和查詢緩存的維護
    StatementHandler封裝了JDBC Statement操作,負責對JDBC statement的操作,如設置參數、將Statement結果集轉換為List集合
    ParameterHandler負責對用戶傳遞的參數轉換為JDBC Statement所需要的參數
    ResultSetHandler負責將JDBC返回的ResultSet結果集對象轉換為List類型的集合
    TypeHandler負責java數據類型和jdbc數據類型之間的映射和轉換
    MappedStatementMappedStatement維護了一條<select、 update 、 delete 、insert >節點的封裝
    SqlSource負責根據用戶傳遞的parameterObject,動態的生成SQL語句,將信息封裝到BoundSql對象中
    BoundSql表示動態生成的SQL語句以及相應的參數信息

    總體流程:

  • 加載配置并初始化
  • 配置來源于兩個地方,一個是配置文件(conf.xml,mapper*.xml),一個是java代碼中的注解,將配置文件內容封裝到Configuration,將sql的配置信息加載成為一個mappedstatement對象,存儲在內存中。
    2. 接收調用請求

    觸發條件:調用Mybatis提供的API

    傳入參數:為SQL的ID和傳入的參數

    將請求傳遞給下層的請求處理層進行處理

  • 處理操作請求
    • 根據SQL的ID查找對應的MappedStatement對象
    • 根據傳入參數對象解析,得到最終要執行的SQL和執行傳入參數
    • 獲取數據庫連接,將最終SQL語句和參數給到數據庫執行,并得到執行結果
    • 根據MappedStatement對象中的結果映射配置對得到的執行結果進行轉換處理,并得到最終的處理結果
    • 釋放連接資源
  • 返回處理結果
  • Mybatis緩存

    Mybatis有一級緩存和二級緩存。Mybatis收到查詢請求后首先會查詢二級緩存,若二級緩存未命中,再去查詢一級緩存,一級緩存沒有,再查詢數據庫。

    一級緩存

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;//從localCache緩存里查數據,沒有就去查數據庫list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}

    這個localCache是BaseExecutor里面的一個屬性

    public abstract class BaseExecutor implements Executor {protected PerpetualCache localCache;

    PerpetualCache類

    public class PerpetualCache implements Cache {private final String id;private Map<Object, Object> cache = new HashMap<Object, Object>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}

    二級緩存

    啟用二級緩存步驟:

  • 開啟cacheEnabled(默認打開)
  • <settings> <setting name="cacheEnabled" value="true"/> </settings>
  • 需要在二級緩存的Mapper配置文件中加入
  • <cache></cache>
  • 注意,二級緩存要想生效,必須要調用sqlSession.commit或close方法
  • public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, parameterObject, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

    注意Cache cache = ms.getCache();,這個cache是從MappedStatement中獲取到的,由于MappedStatement存在全局配置中,可以多個CachingExecutor獲取到,這樣就會出現線程安全問題。除此之外,若不加以控制,多個事務共用一個緩存實例,會導致臟讀的存在。

    那么mybatis是怎么解決臟讀的呢?借用了上面的tcm這個變量,也就是TransactionalCacheManager類來解決的。

    TransactionalCacheManager類維護了Cache,TransactionalCache的關系,真正的數據還是交由TransactionalCache處理的。

    結構如圖:

    public class TransactionalCacheManager {private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();public void clear(Cache cache) {getTransactionalCache(cache).clear();}public Object getObject(Cache cache, CacheKey key) {return getTransactionalCache(cache).getObject(key);}public void putObject(Cache cache, CacheKey key, Object value) {getTransactionalCache(cache).putObject(key, value);}public void commit() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.commit();}}public void rollback() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.rollback();}}private TransactionalCache getTransactionalCache(Cache cache) {TransactionalCache txCache = transactionalCaches.get(cache);if (txCache == null) {txCache = new TransactionalCache(cache);transactionalCaches.put(cache, txCache);}return txCache;}}

    接下來看一下TransactionalCache的代碼

    public class TransactionalCache implements Cache {private static final Log log = LogFactory.getLog(TransactionalCache.class);// 真正的緩存對象private final Cache delegate;private boolean clearOnCommit;//在事務被提交前,所有從數據庫中查詢的結果將緩存在此集合中private final Map<Object, Object> entriesToAddOnCommit;//在事務被提交前,當緩存未命中時,CacheKey 將會被存儲在此集合中private final Set<Object> entriesMissedInCache;public TransactionalCache(Cache delegate) {this.delegate = delegate;this.clearOnCommit = false;this.entriesToAddOnCommit = new HashMap<Object, Object>();this.entriesMissedInCache = new HashSet<Object>();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}@Overridepublic Object getObject(Object key) {// issue #116//獲取緩存的時候從delegate里獲取的Object object = delegate.getObject(key);if (object == null) {//緩存未命中,將key存入entriesMissedInCache.entriesMissedInCache.add(key);}// issue #146if (clearOnCommit) {return null;} else {return object;}}@Overridepublic ReadWriteLock getReadWriteLock() {return null;}@Overridepublic void putObject(Object key, Object object) {//put的時候只是將數據庫的數據放入到了entriesToAddOnCommitentriesToAddOnCommit.put(key, object);}@Overridepublic Object removeObject(Object key) {return null;}@Overridepublic void clear() {clearOnCommit = true;entriesToAddOnCommit.clear();}public void commit() {if (clearOnCommit) {delegate.clear();}//刷新未緩存的結果到delegate中去flushPendingEntries();reset();}public void rollback() {unlockMissedEntries();reset();}private void reset() {clearOnCommit = false;entriesToAddOnCommit.clear();entriesMissedInCache.clear();}private void flushPendingEntries() {for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {delegate.putObject(entry.getKey(), entry.getValue());}for (Object entry : entriesMissedInCache) {if (!entriesToAddOnCommit.containsKey(entry)) {delegate.putObject(entry, null);}}}private void unlockMissedEntries() {for (Object entry : entriesMissedInCache) {try {delegate.removeObject(entry);} catch (Exception e) {log.warn("Unexpected exception while notifiying a rollback to the cache adapter."+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);}}}}

    我們存儲二級緩存的時候是放入到TransactionalCache.entriesToAddOnCommit這個map中,但是每次查詢的時候是從delegate查詢的,所以這個二級緩存查詢數據庫后,緩存是沒有立刻生效的。只有當執行了sqlSession的commit或close方法后,它會調用到tcm的commit,在調用到transactionlCache的commit,刷新緩存到delegate了。

    總結:

    • 二級緩存的設計上,大量運用了裝飾器模式,如SynchronizedCache、LoggingCache。
    • 二級緩存實現了Sqlsession之間的緩存數據共享,屬于namespace級別
    • 二級緩存的實現由CachingExecutor和一個事務型預緩存TransactionlCache完成。

    總結

    以上是生活随笔為你收集整理的深入了解Mybatis架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。