(二十一)Mybatis一級查詢快取
注:程式碼已託管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,專案是mybatis-17-sqlsessionCache,需要自取,需要配置maven環境以及mysql環境(sql語句在resource下的test.sql中),覺得有用可以點個小星星,小菜鳥在此Thanks~
查詢快取的使用,主要是為了提高查詢訪問速度,不用每次都從資料庫查詢資料,可以提高訪問速度,而且可以減少資料庫查詢次數,減小資料庫壓力。
一級查詢快取
1.mybatis一級快取是基於org.apache.ibatis.cache.impl.PerpetualCache類的HashMap本地快取,作用域是SqlSession,也就是在同一個sqlsession中兩次執行相同的sql查詢語句,第一次執行完畢之後,就會將查詢結果寫入快取中,第二次會從快取裡面直接獲取資料,不需要去資料庫查詢。
2.當一個SqlSession結束,這個SqlSession的以及快取就不存在了,mybatis預設開啟一級快取,而且不可以關閉
3.一級快取存的時候是根據sql語句的id,不是根據sql的具體內容。
證明一級快取的存在
在這裡不放大部分程式碼,只放上核心程式碼,sql的介面如下:
public interface IStudentDao {
public Student selectStudentById(int id);
}
mapper.xml與之對應的sql語句:
<!-- 通過id來查詢學生 -->
<select id="selectStudentById" resultType="Student">
select * from student where id=#{xxx}
</select >
單元測試:
@Test
public void testselectStudentById(){
// 第一次查詢
Student student=dao.selectStudentById(17);
System.out.println(student);
// 第二次查詢
Student student2=dao.selectStudentById(17);
System.out.println(student2);
}
結果,我們可以看到我們只執行了一次查詢,第二次查詢的時候直接走的一級快取,沒有對資料庫進行查詢,如果是對資料庫進行查詢,那麼會有sql語句打印出來:
[service] 2018-07-21 09:48:22,534 - dao.IStudentDao.selectStudentById -1349 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 09:48:22,635 - dao.IStudentDao.selectStudentById -1450 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 09:48:22,677 - dao.IStudentDao.selectStudentById -1492 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
Student [id=17, name=hello, age=14, score=94.6]
從快取中讀取資料的依據是sql的id還是sql本身?
我們此時有一個疑問,快取到底是根據什麼進行快取的,那麼我們需要做實驗,寫兩個擁有不同的id但是sql完全一致的sql就知道結果了。下面是在同一個namespace中進行:
sql介面:
public interface IStudentDao {
public Student selectStudentById(int id);
// 測試不同的id的sql
public Student selectStudentById2(int id);
}
mapper檔案:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 把mapper的namespace改成類名的全名,那麼直接呼叫介面的方法的時候才有可能卻找到對應的mapper中的對應的方法 -->
<mapper namespace="dao.IStudentDao">
<!-- 通過id來查詢學生 -->
<select id="selectStudentById" resultType="Student">
select * from student where id=#{xxx}
</select>
<!-- 測試不同id的sql -->
<select id="selectStudentById2" resultType="Student">
select * from student where id=#{xxx}
</select>
</mapper>
測試程式碼:
@Test
public void testDiffereentId2(){
// 第一次查詢
Student student=dao.selectStudentById(17);
System.out.println(student);
// 第二次查詢,測試不同的id,同一個namespace
Student student2=dao.selectStudentById2(17);
System.out.println(student2);
}
結果如下,我們可以看到裡面執行了兩次sql,那麼就證明了一級快取不是依據sql本身來的,而是依據sql的id快取的:
[service] 2018-07-21 10:26:32,844 - dao.IStudentDao.selectStudentById -957 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 10:26:32,954 - dao.IStudentDao.selectStudentById -1067 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 10:26:32,989 - dao.IStudentDao.selectStudentById -1102 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
[service] 2018-07-21 10:26:32,990 - dao.IStudentDao.selectStudentById2 -1103 [main] DEBUG dao.IStudentDao.selectStudentById2 - ==> Preparing: select * from student where id=?
[service] 2018-07-21 10:26:32,991 - dao.IStudentDao.selectStudentById2 -1104 [main] DEBUG dao.IStudentDao.selectStudentById2 - ==> Parameters: 17(Integer)
[service] 2018-07-21 10:26:32,996 - dao.IStudentDao.selectStudentById2 -1109 [main] DEBUG dao.IStudentDao.selectStudentById2 - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
不同的namespace下的相同id呢?
不同的namespace。就算是同一個id,那麼一級快取也是不生效的,因為sql快取的時候是根據namespace不同來區分的。
兩個sql介面如下:
public interface IStudentDao {
public Student selectStudentById(int id);
}
public interface IStudentDao2 {
//不同的namespace下的相同id的測試
public Student selectStudentById(int id);
}
兩個mapper檔案如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 把mapper的namespace改成類名的全名,那麼直接呼叫介面的方法的時候才有可能卻找到對應的mapper中的對應的方法 -->
<mapper namespace="dao.IStudentDao">
<!-- 通過id來查詢學生 -->
<select id="selectStudentById" resultType="Student">
select * from student where id=#{xxx}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 把mapper的namespace改成類名的全名,那麼直接呼叫介面的方法的時候才有可能卻找到對應的mapper中的對應的方法 -->
<mapper namespace="dao.IStudentDao2">
<!-- 通過id來查詢學生 -->
<select id="selectStudentById" resultType="Student">
select * from student where id=#{xxx}
</select>
</mapper>
單元測試如下:
@Test
public void testDiffereentNamespaceSameId(){
// 第一次查詢
Student student=dao.selectStudentById(17);
System.out.println(student);
// 第二次查詢,測試相同的id,不同的namespace
IStudentDao2 dao2= sqlSession.getMapper(IStudentDao2.class);
Student student2=dao2.selectStudentById(17);
System.out.println(student2);
}
結果如下,我們可以看到執行了兩次sql。也就證明了一級快取根據的是同一個namespace下的同一個id,就算是同一個id,但是namespace也不會生效,因為一級快取是根據namespace來區分存放的。:
[service] 2018-07-21 10:37:36,916 - dao.IStudentDao.selectStudentById -1545 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 10:37:37,154 - dao.IStudentDao.selectStudentById -1783 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 10:37:37,194 - dao.IStudentDao.selectStudentById -1823 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
[service] 2018-07-21 10:37:37,202 - dao.IStudentDao2.selectStudentById -1831 [main] DEBUG dao.IStudentDao2.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 10:37:37,204 - dao.IStudentDao2.selectStudentById -1833 [main] DEBUG dao.IStudentDao2.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 10:37:37,210 - dao.IStudentDao2.selectStudentById -1839 [main] DEBUG dao.IStudentDao2.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
增刪改對一級快取的影響
增刪改操作無論是否提交sqlsession.commit(),都會清空以及查詢快取,讓查詢快取再次從DB中查詢。
sql的介面如下:
public interface IStudentDao {
public Student selectStudentById(int id);
// 測試不同的id的sql
public Student selectStudentById2(int id);
// 增加學生
public void insertStudent(Student student);
// 根據id刪除學生
public void deleteStudentById(int id);
// 更新學生的資訊
public void updateStudent(Student student);
}
mapper.xml檔案:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 把mapper的namespace改成類名的全名,那麼直接呼叫介面的方法的時候才有可能卻找到對應的mapper中的對應的方法 -->
<mapper namespace="dao.IStudentDao">
<!-- 通過id來查詢學生 -->
<select id="selectStudentById" resultType="Student">
select * from student where id=#{xxx}
</select>
<!-- 測試不同id的sql -->
<select id="selectStudentById2" resultType="Student">
select * from student where id=#{xxx}
</select>
<insert id="insertStudent" parameterType="Student">
insert into student(name,age,score) values(#{name},#{age},#{score})
</insert>
<!-- 刪除 -->
<delete id="deleteStudentById">
delete from student where id=#{id}
<!-- 這裡的id放什麼都可以,只是一個佔位符,不表示什麼 -->
</delete>
<update id="updateStudent">
update student set name=#{name},age=#{age},score=#{score} where id=#{id}
</update>
</mapper>
單元測試:
public void test03(){
// 第一次查詢
Student student=dao.selectStudentById(17);
System.out.println(student);
//插入學生
Student student1 = new Student("12112",12,21.6);
//dao.insertStudent(student1);
dao.updateStudent(student1);
student=dao.selectStudentById(17);
System.out.println(student);
}
當我們執行第一次查詢之後,執行一次插入操作的時候,我們發現一級快取已經被更新了:
[service] 2018-07-21 13:07:27,136 - dao.IStudentDao.selectStudentById -1059 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 13:07:27,247 - dao.IStudentDao.selectStudentById -1170 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 13:07:27,288 - dao.IStudentDao.selectStudentById -1211 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
[service] 2018-07-21 13:07:27,289 - dao.IStudentDao.insertStudent -1212 [main] DEBUG dao.IStudentDao.insertStudent - ==> Preparing: insert into student(name,age,score) values(?,?,?)
[service] 2018-07-21 13:07:27,291 - dao.IStudentDao.insertStudent -1214 [main] DEBUG dao.IStudentDao.insertStudent - ==> Parameters: 12112(String), 12(Integer), 21.6(Double)
[service] 2018-07-21 13:07:27,295 - dao.IStudentDao.insertStudent -1218 [main] DEBUG dao.IStudentDao.insertStudent - <== Updates: 1
[service] 2018-07-21 13:07:27,295 - dao.IStudentDao.selectStudentById -1218 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 13:07:27,295 - dao.IStudentDao.selectStudentById -1218 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 13:07:27,302 - dao.IStudentDao.selectStudentById -1225 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
當我們執行第一次查詢之後,再執行一次更新操作,就算更新的不是查詢的資料,但是屬於同一張表,一級快取同樣被更新:
[service] 2018-07-21 23:43:28,073 - dao.IStudentDao.selectStudentById -1081 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 23:43:28,202 - dao.IStudentDao.selectStudentById -1210 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 23:43:28,236 - dao.IStudentDao.selectStudentById -1244 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
[service] 2018-07-21 23:43:28,239 - dao.IStudentDao.updateStudent -1247 [main] DEBUG dao.IStudentDao.updateStudent - ==> Preparing: update student set name=?,age=?,score=? where id=?
[service] 2018-07-21 23:43:28,241 - dao.IStudentDao.updateStudent -1249 [main] DEBUG dao.IStudentDao.updateStudent - ==> Parameters: 12112(String), 12(Integer), 21.6(Double), 18(Integer)
[service] 2018-07-21 23:43:28,246 - dao.IStudentDao.updateStudent -1254 [main] DEBUG dao.IStudentDao.updateStudent - <== Updates: 1
[service] 2018-07-21 23:43:28,246 - dao.IStudentDao.selectStudentById -1254 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 23:43:28,246 - dao.IStudentDao.selectStudentById -1254 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 23:43:28,251 - dao.IStudentDao.selectStudentById -1259 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
當我們執行一次查詢操作之後,執行一次刪除操作,那麼一級快取同樣會被更新:
[service] 2018-07-21 23:44:49,296 - dao.IStudentDao.selectStudentById -1172 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 23:44:49,457 - dao.IStudentDao.selectStudentById -1333 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 23:44:49,504 - dao.IStudentDao.selectStudentById -1380 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]
[service] 2018-07-21 23:44:49,505 - dao.IStudentDao.deleteStudentById -1381 [main] DEBUG dao.IStudentDao.deleteStudentById - ==> Preparing: delete from student where id=?
[service] 2018-07-21 23:44:49,505 - dao.IStudentDao.deleteStudentById -1381 [main] DEBUG dao.IStudentDao.deleteStudentById - ==> Parameters: 18(Integer)
[service] 2018-07-21 23:44:49,508 - dao.IStudentDao.deleteStudentById -1384 [main] DEBUG dao.IStudentDao.deleteStudentById - <== Updates: 1
[service] 2018-07-21 23:44:49,509 - dao.IStudentDao.selectStudentById -1385 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Preparing: select * from student where id=?
[service] 2018-07-21 23:44:49,509 - dao.IStudentDao.selectStudentById -1385 [main] DEBUG dao.IStudentDao.selectStudentById - ==> Parameters: 17(Integer)
[service] 2018-07-21 23:44:49,517 - dao.IStudentDao.selectStudentById -1393 [main] DEBUG dao.IStudentDao.selectStudentById - <== Total: 1
Student [id=17, name=hello, age=14, score=94.6]