mybatis精講(六)--二級緩存

簡介

  • 上一章節咱們簡單瞭解了二級緩存的配置。今天咱們詳細分析下二級緩存以及爲何不建議使用二級緩存。算法

  • 一級緩存針對的是sqlsession。二級緩存針對的是namespace層面的。sql

配置

  • 以前咱們已經提到了配置二級緩存以及配置自定義的二級緩存。下面咱們從頭開始實現二級緩存。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
  executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
  executor = new ReuseExecutor(this, transaction);
} else {
  executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
  executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
  • 經過上面的代碼咱們能夠看出來,cacheEnabled這個屬性是控制二級緩存的配置的。而這個屬性在Configuration中默認是true。這裏說明了mybatis默認是開啓緩存功能的。二級緩存和一級緩存的區別其實除了範圍之外,他們的不一樣點就是順序不一樣。真正開啓二級緩存的是在mapper的xml中配置cache標籤就好了。

  • 咱們這裏在StudentMapper.xml中配置.而後咱們在test類中進行獲取兩次sqlsession調用同一個sql.
SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdAndName("1", "1");
System.out.println(student);
SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1");
System.out.println(studentByIdAndName);
  • 可是結果確實很意外。事實上並無只調用一次sql。而是調用了兩次。這僅僅是結果上的異常。咱們用的是Student這個結果接受的。咱們再從代碼層面上看看
@Data
@Builder
@Accessors(chain = true)
public class Student {
    /**
     * 學生索引id
     */
    private String id;
    /**
     * 姓名
     */
    private String userName;

    /**
     * 用戶暱稱
     */
    private String userNick;

    /**
     * 年齡
     */
    private Integer age;
    /**
     * 性別 true : 男  ; false : 女
     */
    private SexEnum sex;
    /**
     * 生日
     */
    private Date birth;
    /**
     * 身高
     */
    private Double height;
}
  • 細心的夥伴也許可以發現。咱們這個實體並無實現序列化。可是以前咱們已經說過了二級緩存的實體須要序列化。按道理來講應該報錯的。這就說明咱們二級緩存開啓,或者確切的說應該說是二級緩存沒有起到做用。
  • 那麼咱們先將實體進行序列化。而後啓動發現並無任何效果。咱們來看看CacheingExecutor.commit()這個方法裏面有事物的提交tcm.commit()

  • 這個地方就是進行緩存存儲的。咱們再來看看mybatis是如何解析mapper.xml中配置的cache標籤的。


  • 由上面代碼咱們得知mybatis會建立一個緩存對象。裏面具體是經過一個build方法來建立的。咱們在來看看build方法裏是啥東西。

  • setStandardDecorators這個方法咱們不知道作啥的。可是熟悉設計模式的都知道Decorator這個詞是裝飾者模式。這裏這個方法也是用來裝飾用的。看看mybatis爲咱們裝飾了那些東西。

  • 首先在newBaseCacheInstance方法中建立原始對象PreprtualCache.而後是加載默認提供的回收機制用的Cache。這個實在build前設置的。
  • 而後就是經過setStandardDecorators進行裝飾了。

  • 因此他的裝飾鏈爲:SynchronizedCache->LogginCache->SerializedCache->LruCache->PerPetualCache設計模式

  • 而在上面的tcm.commit就是在SerializedCache進行緩存對象的。因此咱們以前的代碼是sqlsession沒有提交。因此代碼只要稍微改動下。緩存

SqlSession sqlSession = SqlSessionFactoryUtils.openSqlsession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdAndName("1", "1");
System.out.println(student);
sqlSession.commit();
SqlSession sqlSession1 = SqlSessionFactoryUtils.sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student studentByIdAndName = mapper1.getStudentByIdAndName("1", "1");
System.out.println(studentByIdAndName);

  • SynchronizedCache : 同步Cache.這個類就是保證線程安全。因此他的方法基本上是加上synchronized來保證線程安全的。安全

  • LoggingCache : 日誌。在上面咱們有個日誌是Cache Hit Ratio 0.5 表示二級緩存的命中率。session

  • SerializedCache : 就是用來序列化數據的。mybatis

  • LruCache : 回收cache的算法app

  • PerPetualCache :基本Cache .ide

源碼

CachingExecutor

@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //獲取Cache對象
    Cache cache = ms.getCache();
    if (cache != null) {
      //根據statment配置刷新緩存,默認是insert、update、delete會刷新緩存
      flushCacheIfRequired(ms);
      //二級緩存開啓入口。
      if (ms.isUseCache() && resultHandler == null) {
        //這個方法主要用來處理存儲過程。後續章節說明
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        //經過緩存事物查詢數據
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          //調用委託類查詢數據
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //加入緩存,供下次獲取
          tcm.putObject(cache, key, list);
        }
        return list;
      }
    }
    //沒有開啓二級緩存則繼續往下走
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

缺點

  • 二級緩存由於更加普遍,因此容易形成髒數據。尤爲是在關聯查詢的時候有序沒法控制刷新力度。很容易出現髒讀。

自定義二級緩存

  • 在以前咱們瞭解到的PerpetualCache是緩存鏈上最基本的緩存類。咱們自定義的緩存就是替代這個類的。在mybatis中會現根據咱們註冊進來的類進行實例化。若是沒有則用默認的PerpetualCache這個類做爲基礎緩存類。
相關文章
相關標籤/搜索