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

[TOC]

簡介

  • 上一章節(jié)我們簡單了解了二級緩存的配置设江。今天我們詳細分析下二級緩存以及為什么不建議使用二級緩存悦荒。

  • 一級緩存針對的是sqlsession瓮顽。二級緩存針對的是namespace層面的泌类。

配置

  • 之前我們已經(jīng)提到了配置二級緩存以及配置自定義的二級緩存读跷。下面我們從頭開始實現(xiàn)二級緩存。

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默認是開啟緩存功能的刹孔。二級緩存和一級緩存的區(qū)別其實除了范圍以外啡省,他們的不同點就是順序不同。真正開啟二級緩存的是在mapper的xml中配置cache標(biāo)簽就行了髓霞。

![](https://upload-images.jianshu.io/upload_images/18986887-1a1ed09bd6a95910.jpeg)

  - 我們這里在StudentMapper.xml中配置.然后我們在test類中進行獲取兩次sqlsession調(diào)用同一個sql.

```java

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);

  • 但是結(jié)果確實很意外卦睹。事實上并沒有只調(diào)用一次sql。而是調(diào)用了兩次酸茴。這僅僅是結(jié)果上的異常分预。我們用的是Student這個結(jié)果接受的。我們再從代碼層面上看看
@Data
@Builder
@Accessors(chain = true)
public class Student {
    /**
             * 學(xué)生索引id
             *      */
             *          private String id;
             *              /**
             *                   * 姓名
             *                        */
             *                            private String userName;

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

    /**
             * 年齡
             *      */
             *          private Integer age;
             *              /**
             *                   * 性別 true : 男  薪捍; false : 女
             *                        */
             *                            private SexEnum sex;
             *                                /**
             *                                     * 生日
             *                                          */
             *                                              private Date birth;
             *                                                  /**
             *                                                       * 身高
             *                                                            */
             *                                                                private Double height;
             *                                                                }

  • 細心的伙伴也許能夠發(fā)現(xiàn)笼痹。我們這個實體并沒有實現(xiàn)序列化配喳。但是之前我們已經(jīng)說過了二級緩存的實體需要序列化。按道理來說應(yīng)該報錯的凳干。這就說明我們二級緩存開啟晴裹,或者確切的說應(yīng)該說是二級緩存沒有起到作用。
    • 那么我們先將實體進行序列化救赐。然后啟動發(fā)現(xiàn)并沒有任何效果涧团。我們來看看CacheingExecutor.commit()這個方法里面有事物的提交tcm.commit()
  • 這個地方就是進行緩存存儲的经磅。我們再來看看mybatis是如何解析mapper.xml中配置的cache標(biāo)簽的泌绣。

  • 由上面代碼我們得知mybatis會創(chuàng)建一個緩存對象。里面具體是通過一個build方法來創(chuàng)建的预厌。我們在來看看build方法里是啥東西阿迈。
  • setStandardDecorators這個方法我們不知道做啥的。但是熟悉設(shè)計模式的都知道Decorator這個詞是裝飾者模式轧叽。這里這個方法也是用來裝飾用的苗沧。看看mybatis為我們裝飾了那些東西炭晒。
  • 首先在newBaseCacheInstance方法中創(chuàng)建原始對象PreprtualCache.然后是加載默認提供的回收機制用的Cache待逞。這個實在build前設(shè)置的。
    • 然后就是通過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 表示二級緩存的命中率。

  • SerializedCache : 就是用來序列化數(shù)據(jù)的灶壶。

  • LruCache : 回收cache的算法

  • PerPetualCache :基本Cache .

源碼

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) {
                                              //根據(jù)statment配置刷新緩存肝断,默認是insert、update驰凛、delete會刷新緩存
                                                          flushCacheIfRequired(ms);
                                                                      //二級緩存開啟入口胸懈。
                                                                                  if (ms.isUseCache() && resultHandler == null) {
                                                                                                //這個方法主要用來處理存儲過程。后續(xù)章節(jié)說明
                                                                                                                ensureNoOutParams(ms, boundSql);
                                                                                                                                @SuppressWarnings("unchecked")
                                                                                                                                                //通過緩存事物查詢數(shù)據(jù)
                                                                                                                                                                List<E> list = (List<E>) tcm.getObject(cache, key);
                                                                                                                                                                                if (list == null) {
                                                                                                                                                                                                  //調(diào)用委托類查詢數(shù)據(jù)
                                                                                                                                                                                                                      list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                                                                                                                                                                                                                                          //加入緩存恰响,供下次獲取
                                                                                                                                                                                                                                                              tcm.putObject(cache, key, list);
                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                return list;
                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                                //沒有開啟二級緩存則繼續(xù)往下走
                                                                                                                                                                                                                                                                                                                                        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                                                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                ```
                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                # 缺點
                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                  - 二級緩存因為更加廣泛趣钱,所以容易造成臟數(shù)據(jù)。尤其是在關(guān)聯(lián)查詢的時候有序無法控制刷新力度胚宦。很容易出現(xiàn)臟讀首有。

![](https://upload-images.jianshu.io/upload_images/18986887-a6ea95327a9d5af8.jpeg)

# 自定義二級緩存

  - 在之前我們了解到的`PerpetualCache`是緩存鏈上最基本的緩存類燕垃。我們自定義的緩存就是替代這個類的。在mybatis中會現(xiàn)根據(jù)我們注冊進來的類進行實例化井联。如果沒有則用默認的`PerpetualCache`這個類作為基礎(chǔ)緩存類卜壕。
  - 
[加入戰(zhàn)隊](#addMe)

# # <span id="addMe">加入戰(zhàn)隊</span>

## 微信公眾號

![微信公眾號](https://upload-images.jianshu.io/upload_images/18986887-7ccd1f8c1804312a.jpeg)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烙常,隨后出現(xiàn)的幾起案子轴捎,更是在濱河造成了極大的恐慌,老刑警劉巖蚕脏,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侦副,死亡現(xiàn)場離奇詭異,居然都是意外死亡驼鞭,警方通過查閱死者的電腦和手機秦驯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來终议,“玉大人汇竭,你說我怎么就攤上這事⊙ㄕ牛” “怎么了细燎?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長皂甘。 經(jīng)常有香客問我玻驻,道長,這世上最難降的妖魔是什么偿枕? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任璧瞬,我火速辦了婚禮,結(jié)果婚禮上渐夸,老公的妹妹穿的比我還像新娘嗤锉。我一直安慰自己,他們只是感情好墓塌,可當(dāng)我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布瘟忱。 她就那樣靜靜地躺著,像睡著了一般苫幢。 火紅的嫁衣襯著肌膚如雪访诱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天韩肝,我揣著相機與錄音触菜,去河邊找鬼。 笑死哀峻,一個胖子當(dāng)著我的面吹牛涡相,可吹牛的內(nèi)容都是我干的哲泊。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼漾峡,長吁一口氣:“原來是場噩夢啊……” “哼攻旦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起生逸,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤牢屋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后槽袄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烙无,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年遍尺,在試婚紗的時候發(fā)現(xiàn)自己被綠了截酷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡乾戏,死狀恐怖迂苛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鼓择,我是刑警寧澤三幻,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站呐能,受9級特大地震影響念搬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摆出,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一朗徊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偎漫,春花似錦爷恳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至通危,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灌曙,已是汗流浹背菊碟。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留在刺,地道東北人逆害。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓头镊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親魄幕。 傳聞我的和親對象是個殘疾皇子相艇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內(nèi)容