什么是一級緩存
一般提到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 從入門到精通 作者:劉增輝