MyBatis 緩存配置之一級緩存


什么是一級緩存

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

MyBatis 的一級緩存機制

MyBatis 的一級緩存存在於 SqlSession 的生命周期中,在同一個 SqlSession 中查詢時,MyBatis 會把執行的方法和參數通過算法生成緩存的鍵值,將鍵值和查詢結果存放如一個 Map 對象中。如果同一個 SqlSession 中執行的方法和參數完全一致,那么通過算法會生成相同的鍵值,當 Map 緩存對象中已經存在該鍵值時,則會返回緩存中的對象。

一級緩存

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

}

運行結果

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對象進行了二次查詢。第一次執行 selectUserByPrimaryKey 方法獲取 User 數據時,真正執行了數據庫查詢,得到了 user1 的結果。第二次執行獲取 user2 的時候,從日志可以看到,只有一次查詢,也就是說第二次查詢並沒有執行數據庫操作。

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

}

運行結果

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 的值,之后沒有進行任何更新數據庫的操作。在獲取 user2 對象后,發現 user2 對象的 name 值竟然和 user1 重新設置后的值一樣。在往下可以發現,原來 user1 和 user2 竟然是同一個對象,之所以這樣就是因為 MyBatis 的一級緩存。

在使用 MyBatis 的過程中,要避免在使用如上代碼中的 user2 時出現的錯誤。我們可能以為獲取的 user2 應該是數據庫中的數據,卻不知道 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 后,會在查詢數據前清空當前一級緩存。因此該方法每次查詢都會從數據庫中獲取數據,這時 user1 和 user2 就是兩個不同的對象,可以避免上面的問題。但是由於這個方法清空了一級緩存,會影響當前 SqlSession 中所有緩存的查詢,因此在需要反復查詢獲取只讀數據的情況下,會增加數據庫的查詢次數,所以要避免這么使用。

一級緩存失效

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

}

運行結果

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

}

運行結果

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

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

例3:SqlSession 相同,兩次查詢之間執行了增、刪、改操作。

測試代碼

/**

 * 一級緩存失效: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();

}

運行結果

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此數據庫查詢。

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

}

運行結果

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

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

內容來自:https://www.jianshu.com/p/6d6b685ed4c3

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM