mybatis緩存使用

緩存的定義

緩存一般是orm框架會提供的功能,目的是提升查詢效率和減少數(shù)據(jù)庫的壓力华匾。Mybatis有1級緩存和2級緩存鳞尔,并且還有集成第三方的緩存工具比如說 redis。

一級緩存

一級緩存是默認開啟的不需要配置亚脆。同時因為它是sqlSession級別的也叫會話緩存。在同一個會話里面盲泛,多次執(zhí)行相同的 SQL 語句濒持,會直接從內(nèi)存取到緩存的結(jié)果,不會再發(fā)送 SQL 到數(shù)據(jù)庫寺滚。但是不同的會話里面柑营,即使執(zhí)行的 SQL 一模一樣(通過一個 Mapper 的同一個方法的相同參數(shù)調(diào)用),也不能使用到一級緩存村视。

命中緩存

@Test
public void selectListCacheOne() {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = userMapper1.selectList();

    UserMapper userMapper2 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList2 = userMapper2.selectList();
    
}

返回結(jié)果是:

Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5ccddd20]
==>  Preparing: select * from user 
==> Parameters: 
interface java.util.List

上面的代碼同一個sqlSession中執(zhí)行2次查詢 只打印出了一次sql語句官套。可見第2次查詢命中了緩存。

未命中緩存

@Test
public void selectListNoneCacheOne() {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = userMapper1.selectList();

    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    List<User> userList2 = userMapper2.selectList();

}

返回結(jié)果

==>  Preparing: select * from user 
==> Parameters: 
interface java.util.List

==>  Preparing: select * from user 
==> Parameters: 
interface java.util.List

2個sqlsession執(zhí)行了2次查詢而sql語句打印了2次虏杰。由此可見不同不同的sqlsession查詢同一個sql不會命中緩存。

一級緩存的弊端

我們知道一級緩存是sqlsession級別的勒虾。那么我在方法1中用sqslsession查詢語句

image-20200623110132109.png

同時打上斷點纺阔。在這個時候執(zhí)行另一個線程插入一條數(shù)據(jù)

@Test
public void mybatisMapperInsert() {
    //4 獲取sqlSession SqlSession 提供了在數(shù)據(jù)庫執(zhí)行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執(zhí)行已映射的 SQL 語句
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //5 獲取sql 通過代理模式 代理了一個映射器
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User userParam = new User();
    userParam.setAge(1);
    userParam.setSexState(SexEnum.MAN);
    userParam.setSexStateOrigin(SexEnumOrigin.MAN);
    userMapper.insert(userParam);
    sqlSession.commit();
}

然后這個時候再次執(zhí)行方法1中的線程修然。

image-20200623110356167.png

可以看到在同一個sqlsession中命中了緩存笛钝,第2次查詢結(jié)果是還是13個。但是數(shù)據(jù)庫中明明是14條數(shù)據(jù)愕宋。所以可見多線程的情況下 一級緩存會出現(xiàn)臟讀的情況玻靡。 那么怎么避免呢?用2級緩存

二級緩存

二級緩存是用來解決一級緩存不能跨會話共享的問題的中贝,范圍是 namespace 級別 的囤捻,可以被多個 SqlSession 共享(只要是同一個接口里面的相同方法,都可以共享)邻寿, 生命周期和應用同步蝎土。作為一個作用范圍更廣的緩存,它肯定是在 SqlSession 的外層绣否,否則不可能被多個 SqlSession 共享誊涯。而一級緩存是在 SqlSession 內(nèi)部的,也就是只有取不到二級緩存的情況下才到一個會話中去取一級緩存蒜撮。

image-20200623111635652.png

開啟二級緩存

在想要開啟二級緩存的Mapper中添加表橋<cache/>

<mapper namespace="com.mybatis.demo.mybatis.mapper.UserMapper">

    <cache eviction="LRU" flushInterval="600000" size="1024" readOnly="true"/>

?

屬性 含義 取值
type 2級緩存的實現(xiàn)類 默認是需要實現(xiàn) Cache 接口暴构,默認是 PerpetualCache
size 最多緩存對象數(shù)量 默認是1024
eviction 回收策略 LRU(默認) FIFO SOFT WEAK
flushInterval 定時自動清理緩存 自動刷新時間,單位 ms段磨,未配置時只有調(diào)用時刷新
readOnly 是否只讀 false 默認 crud操作會更新緩存 true:只讀緩存 性能提高
blocking 是否使用可重入鎖實現(xiàn) 緩存的并發(fā)控制 true取逾,會使用 BlockingCache 對 Cache 進行裝飾 默認 false

Mapper.xml 配置了<cache>之后,select()會被緩存薇溃。update()菌赖、delete()、insert() 會刷新緩存沐序。

如果某些查詢方法對數(shù)據(jù)的實時性要求很高琉用,不需要二級緩存,怎么辦? 我們可以在單個 Statement ID 上顯式關(guān)閉二級緩存(默認是 true):

<select id="selectList"  resultMap="BaseResultMap" useCache="false">
     select * from user
</select>

這樣子在命名空間com.mybatis.demo.mybatis.mapper.UserMapper就開啟了一個緩存

例子

@Test
public void selectListCacheTwoUpdate() {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = userMapper1.selectList();
    sqlSession1.close();

    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    List<User> userList2= userMapper2.selectList();
    sqlSession2.close();
}
返回結(jié)果
Cache Hit Ratio [com.mybatis.demo.mybatis.mapper.UserMapper]: 0.5

打印出這個信息代表了命中緩存策幼。

集成redis緩存

mybatis提供了集成redis的緩存 引入maven

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-redis</artifactId>
    <version>1.0.0-beta2</version>
</dependency>

mapper中添加緩存的type

<cache type="org.mybatis.caches.redis.RedisCache"  flushInterval="60000" size="512" />

執(zhí)行的效果與上面的一樣

spring+redis+mybatis緩存

mybatis雖然提供了redis緩存但是沒有提供對spring的支持邑时。所以想要在spring緩存下使用需要自己實現(xiàn)對myabtis緩存的支持需要集成接口 org.apache.ibatis.cache.Cache

@Component
public class MybatisRedisCache implements Cache {

  
    /**
     * ID
     */
    private String id;

    /**
     * 集成redisTemplate
     */
    private  static RedisTemplate redisTemplate;


    public MybatisRedisCache() {
    }

    public MybatisRedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        } else {
            this.id = id;
        }
    }


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

    @Override
    public int getSize() {
        try {
            Long size = redisTemplate.opsForHash().size(this.id.toString());
            return size.intValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public void putObject(final Object key, final Object value) {
        try {
            redisTemplate.opsForHash().put(this.id.toString(), key.toString(), value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getObject(final Object key) {
        try {
            Object hashVal = redisTemplate.opsForHash().get(this.id.toString(), key.toString());
            return hashVal;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Object removeObject(final Object key) {
        try {
            redisTemplate.opsForHash().delete(this.id.toString(), key.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void clear() {
        try {
            redisTemplate.delete(this.id.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "MybatisRedisCache {" + this.id + "}";
    }


    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        MybatisRedisCache.redisTemplate = redisTemplate;
    }


}

private static RedisTemplate redisTemplate 為了集成spring redis,實現(xiàn)的緩存類中需要靜態(tài)注入redisTemplate特姐。通過靜態(tài)方法

public class RedisCacheTransfer {


    @Autowired
    public void serRedisTemplate(RedisTemplate redisTemplate) {
        MybatisRedisCache.setRedisTemplate(redisTemplate);
    }
@Bean
public RedisCacheTransfer redisCacheTransfer(RedisTemplate redisTemplate) {
    RedisCacheTransfer redisCacheTransfer = new RedisCacheTransfer();
    redisCacheTransfer.serRedisTemplate(redisTemplate);
    return redisCacheTransfer;
}

結(jié)果

image-20200628165800830.png

cache 有多個實例晶丘,采用的裝飾器模式

2級緩存 要等session關(guān)閉才會放入到緩存中 這是為什么

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子浅浮,更是在濱河造成了極大的恐慌沫浆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滚秩,死亡現(xiàn)場離奇詭異专执,居然都是意外死亡,警方通過查閱死者的電腦和手機郁油,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門本股,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人桐腌,你說我怎么就攤上這事拄显。” “怎么了案站?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵躬审,是天一觀的道長。 經(jīng)常有香客問我嚼吞,道長盒件,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任舱禽,我火速辦了婚禮炒刁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘誊稚。我一直安慰自己翔始,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布里伯。 她就那樣靜靜地躺著城瞎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疾瓮。 梳的紋絲不亂的頭發(fā)上脖镀,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音狼电,去河邊找鬼蜒灰。 笑死,一個胖子當著我的面吹牛肩碟,可吹牛的內(nèi)容都是我干的强窖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼削祈,長吁一口氣:“原來是場噩夢啊……” “哼翅溺!你這毒婦竟也來了脑漫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咙崎,失蹤者是張志新(化名)和其女友劉穎优幸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體褪猛,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡劈伴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了握爷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡严里,死狀恐怖新啼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刹碾,我是刑警寧澤燥撞,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站迷帜,受9級特大地震影響物舒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜戏锹,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一冠胯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锦针,春花似錦荠察、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馋吗,卻和暖如春焕盟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宏粤。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工脚翘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人商架。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓堰怨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛇摸。 傳聞我的和親對象是個殘疾皇子备图,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354