亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java Mybatis架構(gòu)設(shè)計(jì)深入了解

 更新時間:2021年11月03日 11:10:38   作者:女友在高考  
在本篇文章里小編給大家整理的是一篇關(guān)于Java Mybatis架構(gòu)設(shè)計(jì)詳解內(nèi)容,對此有興趣的朋友們可以參考下,希望能夠給你帶來幫助

架構(gòu)設(shè)計(jì)

我們可以把Mybatis的功能架構(gòu)分為三層:

1.API接口層:提供給外部使用的接口API,開發(fā)人員通過這些本地API來操縱數(shù)據(jù)庫。接口層一接收到調(diào)用請求就會調(diào)用數(shù)據(jù)處理層來完成具體的數(shù)據(jù)處理。

Mybatis和數(shù)據(jù)庫的交互有兩種方式:

  1. 使用傳統(tǒng)的Mybatis提供API
  2. 使用Mapper代理的方式

2.數(shù)據(jù)處理層:負(fù)責(zé)具體的SQL查找、SQL解析、SQL執(zhí)行和執(zhí)行結(jié)果映射處理等。他主要的目的是根據(jù)調(diào)用的請求完成一次數(shù)據(jù)庫操作。

3.基礎(chǔ)支撐層:負(fù)責(zé)最基礎(chǔ)的功能支撐,包括連接管理、事務(wù)管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來最為基礎(chǔ)組件。為上層的數(shù)據(jù)處理層提供最基礎(chǔ)的支撐。

Mybatis主要構(gòu)件

構(gòu)件 描述
SqlSession 作為Mybatis工作的主要頂層API,表示和數(shù)據(jù)庫交互的會話,完成必要數(shù)據(jù)庫增刪查改功能
Executor Mybatis執(zhí)行器,是Mybatis調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩存的維護(hù)
StatementHandler 封裝了JDBC Statement操作,負(fù)責(zé)對JDBC statement的操作,如設(shè)置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換為List集合
ParameterHandler 負(fù)責(zé)對用戶傳遞的參數(shù)轉(zhuǎn)換為JDBC Statement所需要的參數(shù)
ResultSetHandler 負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對象轉(zhuǎn)換為List類型的集合
TypeHandler 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換
MappedStatement MappedStatement維護(hù)了一條<select、 update 、 delete 、insert >節(jié)點(diǎn)的封裝
SqlSource 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動態(tài)的生成SQL語句,將信息封裝到BoundSql對象中
BoundSql 表示動態(tài)生成的SQL語句以及相應(yīng)的參數(shù)信息

總體流程:

1.加載配置并初始化

配置來源于兩個地方,一個是配置文件(conf.xml,mapper*.xml),一個是java代碼中的注解,將配置文件內(nèi)容封裝到Configuration,將sql的配置信息加載成為一個mappedstatement對象,存儲在內(nèi)存中。

2. 接收調(diào)用請求

觸發(fā)條件:調(diào)用Mybatis提供的API

傳入?yún)?shù):為SQL的ID和傳入的參數(shù)

將請求傳遞給下層的請求處理層進(jìn)行處理

3.處理操作請求

  • 根據(jù)SQL的ID查找對應(yīng)的MappedStatement對象
  • 根據(jù)傳入?yún)?shù)對象解析,得到最終要執(zhí)行的SQL和執(zhí)行傳入?yún)?shù)
  • 獲取數(shù)據(jù)庫連接,將最終SQL語句和參數(shù)給到數(shù)據(jù)庫執(zhí)行,并得到執(zhí)行結(jié)果
  • 根據(jù)MappedStatement對象中的結(jié)果映射配置對得到的執(zhí)行結(jié)果進(jìn)行轉(zhuǎn)換處理,并得到最終的處理結(jié)果
  • 釋放連接資源

4.返回處理結(jié)果

Mybatis緩存

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

一級緩存

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緩存里查數(shù)據(jù),沒有就去查數(shù)據(jù)庫
      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 #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    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;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

二級緩存

啟用二級緩存步驟:

1.開啟cacheEnabled(默認(rèn)打開)

<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

2.需要在二級緩存的Mapper配置文件中加入

<cache></cache>

3.注意,二級緩存要想生效,必須要調(diào)用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獲取到,這樣就會出現(xiàn)線程安全問題。除此之外,若不加以控制,多個事務(wù)共用一個緩存實(shí)例,會導(dǎo)致臟讀的存在。

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

TransactionalCacheManager類維護(hù)了Cache,TransactionalCache的關(guān)系,真正的數(shù)據(jù)還是交由TransactionalCache處理的。

結(jié)構(gòu)如圖:

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;
  //在事務(wù)被提交前,所有從數(shù)據(jù)庫中查詢的結(jié)果將緩存在此集合中
  private final Map<Object, Object> entriesToAddOnCommit;
  //在事務(wù)被提交前,當(dāng)緩存未命中時,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>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  @Override
  public Object getObject(Object key) {
    // issue #116
    //獲取緩存的時候從delegate里獲取的
    Object object = delegate.getObject(key);
    if (object == null) {
      //緩存未命中,將key存入entriesMissedInCache.
      entriesMissedInCache.add(key);
    }
    // issue #146
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public void putObject(Object key, Object object) {
    //put的時候只是將數(shù)據(jù)庫的數(shù)據(jù)放入到了entriesToAddOnCommit
    entriesToAddOnCommit.put(key, object);
  }

  @Override
  public Object removeObject(Object key) {
    return null;
  }

  @Override
  public void clear() {
    clearOnCommit = true;
    entriesToAddOnCommit.clear();
  }

  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    //刷新未緩存的結(jié)果到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查詢的,所以這個二級緩存查詢數(shù)據(jù)庫后,緩存是沒有立刻生效的。只有當(dāng)執(zhí)行了sqlSession的commit或close方法后,它會調(diào)用到tcm的commit,在調(diào)用到transactionlCache的commit,刷新緩存到delegate了。

總結(jié):

二級緩存的設(shè)計(jì)上,大量運(yùn)用了裝飾器模式,如SynchronizedCache、LoggingCache。

二級緩存實(shí)現(xiàn)了Sqlsession之間的緩存數(shù)據(jù)共享,屬于namespace級別

二級緩存的實(shí)現(xiàn)由CachingExecutor和一個事務(wù)型預(yù)緩存TransactionlCache完成。

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 關(guān)于java中構(gòu)造函數(shù)的一些知識詳解

    關(guān)于java中構(gòu)造函數(shù)的一些知識詳解

    下面小編就為大家?guī)硪黄P(guān)于java中構(gòu)造函數(shù)的一些知識詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • 詳解Java8與Runtime.getRuntime().availableProcessors()

    詳解Java8與Runtime.getRuntime().availableProcessors()

    這篇文章主要介紹了詳解Java8與Runtime.getRuntime().availableProcessors(),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 詳解Maven倉庫之本地倉庫、遠(yuǎn)程倉庫

    詳解Maven倉庫之本地倉庫、遠(yuǎn)程倉庫

    這篇文章主要介紹了Maven倉庫之本地倉庫、遠(yuǎn)程倉庫,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • SpringBoot如何優(yōu)雅地處理全局異常詳解

    SpringBoot如何優(yōu)雅地處理全局異常詳解

    這篇文章主要給大家介紹了關(guān)于SpringBoot如何優(yōu)雅地處理全局異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • java IO 文件操作方法總結(jié)

    java IO 文件操作方法總結(jié)

    這篇文章主要介紹了java IO 文件操作方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • java根據(jù)方法名稱取得反射方法的參數(shù)類型示例

    java根據(jù)方法名稱取得反射方法的參數(shù)類型示例

    利用java反射原理調(diào)用方法時,常先需要傳入方法參數(shù)數(shù)組才能取得方法。該方法參數(shù)數(shù)組采用動態(tài)取得的方式比較合適
    2014-02-02
  • Java ArrayList 數(shù)組之間相互轉(zhuǎn)換

    Java ArrayList 數(shù)組之間相互轉(zhuǎn)換

    本文通過代碼示例給大家講解arraylist轉(zhuǎn)化為數(shù)組,然后數(shù)組轉(zhuǎn)化為arraylist的相關(guān)資料,感興趣的朋友一起看看吧
    2015-11-11
  • Java開發(fā)中常用的 Websocket 技術(shù)參考

    Java開發(fā)中常用的 Websocket 技術(shù)參考

    WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動向客戶端推送數(shù)據(jù),當(dāng)然也支持客戶端發(fā)送數(shù)據(jù)到服務(wù)端。
    2020-09-09
  • Spring AOP日志框架實(shí)現(xiàn)過程圖解

    Spring AOP日志框架實(shí)現(xiàn)過程圖解

    這篇文章主要介紹了Spring AOP日志框架實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • MyBatis中有關(guān)int和Integer的使用方式

    MyBatis中有關(guān)int和Integer的使用方式

    這篇文章主要介紹了MyBatis中有關(guān)int和Integer的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03

最新評論