MyBatis 緩存配置 之 一級緩存

什么是一級緩存

一般提到MyBatis緩存的時候,都是指二級緩存遮精。一級緩存 (也叫本地緩存)默認會啟用酬土,并且不能控制,因此很少會提到啃擦。

MyBatis 的一級緩存機制

MyBatis 的一級緩存存在于 SqlSession 的生命周期中囊蓝,在同一個 SqlSession 中查詢時,MyBatis 會把執(zhí)行的方法和參數(shù)通過算法生成緩存的鍵值令蛉,將鍵值和查詢結(jié)果存放如一個 Map 對象中聚霜。如果同一個 SqlSession 中執(zhí)行的方法和參數(shù)完全一致狡恬,那么通過算法會生成相同的鍵值,當 Map 緩存對象中已經(jīng)存在該鍵值時蝎宇,則會返回緩存中的對象弟劲。

一級緩存

例1:一級緩存的效果演示

測試代碼

/**
 * 一級緩存
 */
@Test
public void testFirstLevelCache() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user1);

    User user2 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user2);
    
    System.out.println(user1 == user2);
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
user1:User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
user2:User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

Process finished with exit code 0

從上面的測試代碼中可以看出,我對id為2的User對象進行了二次查詢姥芥。第一次執(zhí)行 selectUserByPrimaryKey 方法獲取 User 數(shù)據(jù)時兔乞,真正執(zhí)行了數(shù)據(jù)庫查詢,得到了 user1 的結(jié)果凉唐。第二次執(zhí)行獲取 user2 的時候庸追,從日志可以看到,只有一次查詢台囱,也就是說第二次查詢并沒有執(zhí)行數(shù)據(jù)庫操作淡溯。

例2:使用一級緩存需要注意點

測試代碼

/**
 * 一級緩存注意點
 */
@Test
public void testL1Cache() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user1);
    user1.setName("xd");
    User user2 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user2);
    System.out.println(user1 == user2);

    sqlSession.close();
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
User(id=2, name=xd, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
true

Process finished with exit code 0

從測試代碼來看,獲取 user1 后重新設置了 name 的值簿训,之后沒有進行任何更新數(shù)據(jù)庫的操作咱娶。在獲取 user2 對象后,發(fā)現(xiàn) user2 對象的 name 值竟然和 user1 重新設置后的值一樣煎楣。在往下可以發(fā)現(xiàn),原來 user1 和 user2 竟然是同一個對象车伞,之所以這樣就是因為 MyBatis 的一級緩存择懂。

在使用 MyBatis 的過程中,要避免在使用如上代碼中的 user2 時出現(xiàn)的錯誤另玖。我們可能以為獲取的 user2 應該是數(shù)據(jù)庫中的數(shù)據(jù)困曙,卻不知道 user1 的一個重新賦值會影響到 user2。

如果不想讓 selectUserByPrimaryKey 方法使用一級緩存谦去,可以做如下修改慷丽。

<select id="selectUserByPrimaryKey" resultMap="BaseResultMap" parameterType="long" flushCache="true">
    select
      <include refid="base_column"/>
    from
      `db_user`
    where
      id = #{key}
</select>

該修改在原來方法的 <select> 標簽中添加 flushCache="true" 屬性,當這個屬性配置為 true 后鳄哭,會在查詢數(shù)據(jù)前清空當前一級緩存要糊。因此該方法每次查詢都會從數(shù)據(jù)庫中獲取數(shù)據(jù),這時 user1 和 user2 就是兩個不同的對象妆丘,可以避免上面的問題锄俄。但是由于這個方法清空了一級緩存,會影響當前 SqlSession 中所有緩存的查詢勺拣,因此在需要反復查詢獲取只讀數(shù)據(jù)的情況下奶赠,會增加數(shù)據(jù)庫的查詢次數(shù),所以要避免這么使用药有。

一級緩存失效

例1:不是同一個SqlSession毅戈。

測試代碼

/**
 * 一級緩存失效:不是同一個SqlSession
 */
@Test
public void testCacheMissForOne() {
    SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
    SqlSession sqlSession2 = MyBatisUtils.getSqlSession();

    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

    User user1 = mapper1.selectUserByPrimaryKey(2L);
    System.out.println(user1);

    User user2 = mapper2.selectUserByPrimaryKey(2L);
    System.out.println(user2);

    sqlSession1.close();
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

Process finished with exit code 0

從上面的測試代碼可以看出,兩次查詢的 mapper 是從兩個不同的 SqlSession 獲取的,而 MyBatis 的一級緩存是依賴 SqlSession 的生命周期的苇经,所以兩個不同的 SqlSession 的一級緩存是不同的赘理。

例2:SqlSession 相同,查詢條件不同塑陵。

測試代碼

/**
 * 一級緩存失效:SqlSession相同感憾,查詢條件不同。
 */
@Test
public void testCacheMissForTwo() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user1);
    User user2 = mapper.selectUserByPrimaryKey(3L);
    System.out.println(user2);

    sqlSession.close();
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 3(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 3, batch1, a1, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=3, name=batch1, password=a1, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

Process finished with exit code 0

從上面測試代碼可以看出令花,二次查詢的條件是不一樣的阻桅,也就是說第二次查詢的數(shù)據(jù)在一級緩存中是不存在的,所以兩次查詢操作都是從數(shù)據(jù)庫獲取數(shù)據(jù)兼都。

例3:SqlSession 相同嫂沉,兩次查詢之間執(zhí)行了增、刪扮碧、改操作趟章。

測試代碼

/**
 * 一級緩存失效:SqlSession相同,兩次查詢之間進行了增慎王、刪蚓土、改操作。
 */
@Test
public void testCacheMissForThree() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user1);

    User user = new User();
    user.setName("hotchXX");
    user.setPassword("xxx");
    user.setPhone("xxx");
    user.setEmail("123@123.com");
    user.setInfo("xxx");
    user.setImg(new byte[]{'a','v','d'});
    user.setCreateTime(new Date());
    mapper.insertUser(user);

    User user2 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user2);

    sqlSession.commit();
    sqlSession.close();
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
DEBUG [main] - ==>  Preparing: insert into `db_user`(user_name, user_password, user_phone, user_email, user_info, user_img, create_time) values (?, ?, ?, ?, ?, ?, ?) 
DEBUG [main] - ==> Parameters: hotchzz(String), zzz(String), zzz(String), 123@123.com(String), zzz(String), [B@4facf68f(byte[]), 2018-08-09 15:25:35.739(Timestamp)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

Process finished with exit code 0

從上面的測試代碼可以看出在兩次查詢操作之間進行了增加操作赖淤,所以即使兩次查詢都是查詢id為2的對象蜀漆,但是還是進行了2此數(shù)據(jù)庫查詢。

例4:SqlSession 相同咱旱,手動清除一級緩存组哩。

測試代碼

/**
 * 一級緩存失效:SqlSession相同忽你,手動清除了一級緩存。
 */
@Test
public void testCacheMissForFour() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user1);

    sqlSession.clearCache();// 清除一級緩存

    User user2 = mapper.selectUserByPrimaryKey(2L);
    System.out.println(user2);

    sqlSession.close();
}

運行結(jié)果

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ? 
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time
TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

Process finished with exit code 0

從上面的測試代碼可以看出,在兩次查詢操作之前執(zhí)行了清除一級緩存操作sqlSession.clearCache();// 清除一級緩存 位仁,所以第二次查詢還是從數(shù)據(jù)庫獲取數(shù)據(jù)鲜结。

參考資料:MyBatis 從入門到精通 作者:劉增輝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末媒至,一起剝皮案震驚了整個濱河市今膊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狐粱,老刑警劉巖赘阀,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脑奠,居然都是意外死亡基公,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門宋欺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轰豆,“玉大人胰伍,你說我怎么就攤上這事∷嵝荩” “怎么了骂租?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斑司。 經(jīng)常有香客問我渗饮,道長,這世上最難降的妖魔是什么宿刮? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任互站,我火速辦了婚禮,結(jié)果婚禮上僵缺,老公的妹妹穿的比我還像新娘胡桃。我一直安慰自己,他們只是感情好磕潮,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布翠胰。 她就那樣靜靜地躺著,像睡著了一般自脯。 火紅的嫁衣襯著肌膚如雪之景。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天膏潮,我揣著相機與錄音锻狗,去河邊找鬼。 笑死戏罢,一個胖子當著我的面吹牛屋谭,可吹牛的內(nèi)容都是我干的脚囊。 我是一名探鬼主播龟糕,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼悔耘!你這毒婦竟也來了讲岁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤衬以,失蹤者是張志新(化名)和其女友劉穎缓艳,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體看峻,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡阶淘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了互妓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溪窒。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡坤塞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澈蚌,到底是詐尸還是另有隱情摹芙,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布宛瞄,位于F島的核電站浮禾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏份汗。R本人自食惡果不足惜盈电,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裸影。 院中可真熱鬧挣轨,春花似錦、人聲如沸轩猩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽均践。三九已至晤锹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彤委,已是汗流浹背鞭铆。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焦影,地道東北人车遂。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像斯辰,于是被迫代替她去往敵國和親舶担。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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