MyBatis一級快取和二級快取
“花明月暗籠輕霧,今宵好向郎邊去”
前言
在實際專案開發中,通常對資料庫查詢的效能要求很高,而 Mybatis提供了查詢快取來快取資料,從而達到提高查詢效能的要求。 Mybatis的査詢快取分為一級快取和二級快取。一級快取是 SqlSession級別的快取,二級快取是mapper級別的快取,二級快取是多個 SqlSession共享的。 Mybatis通過快取機制減輕資料壓力,提高資料庫效能。
- 一級快取( SqlSession級別) Mybatis的一級快取是 SqlSession級別的快取。在操作資料庫時需要構造 SqlSession物件,在物件中有一個 HashMap用於儲存快取資料。不同的 SqlSession之間的快取資料區域( Hashmap)是互相不影響的。 一級快取的作用域是 SqlSession範圍的,當在同一個 SqlSession中執行兩次相同的sql語句時,第一次執行完畢會將資料庫中查詢的資料寫到快取(記憶體),第二次査詢時會從快取中獲取資料,不再去底層資料庫查詢,從而提高查詢效率。需要注意的是,如果 SqlSession執行了DML操作( Insert、update和delete),並提交到資料庫, MyBatis I則會清空SqlSession中的一級快取,這樣做的目的是為了保證快取中儲存的是最新的資訊,避免出現髒讀現象。 當一個 SqlSession結東後該 SqlSession中的一級快取也就不存在了。 Mybatis預設開啟一級快取,不需要進行任何配置。
- 二級快取(mapper級別)
二級快取是mapper級別的快取。使用二級快取時,多個 SqlSession使用同一個 Mapper的sql語句去操作資料庫,得到的資料會存在二級快取區域,它同樣是使用 HashMap進行資料儲存。相比一級快取 SqlSession,二級快取的範圍更大,多個 SqlSession可以共用二級快取,二級快取是跨 SqlSession的。
二級快取是多個 SqlSession共享的,其作用域是 mapper的同一個 namespace。不同的SqlSession兩次執行相同的 namespace下的sql語句,且向sql中傳遞的引數也相同,即最終執行相同的sql語句,則第一次執行完畢會將資料庫中査詢的資料寫到快取(記憶體),第二次查詢時會從快取中獲取資料,不再去底層資料庫查詢,從而提高查詢效率。
MyBatis預設沒有開啟二級快取,需要在 setting全域性引數中配置開啟二級快取。
注意:
例項
- 測試一級快取 一級快取時MyBatis的預設快取,所以無需進行其快取的配置。
UserMapper.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="org.arunner.mapper.UserMapper"> <!--根據ID查詢User--> <select id="selectUserById" parameterType="int" resultType="org.arunner.domain.User"> select * from tb_user where id = #{id} ; </select> <!--查詢所有user--> <select id="selectAllUser" resultType="org.arunner.domain.User"> select * from tb_user ; </select> <!--根據ID刪除User--> <delete id="deleteUserById" parameterType="int"> delete from tb_user where id = #{id}; </delete> </mapper>
測試類:
import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;
/**
* @author arunner
* @date 2018/11/10
*/
public class TestOneLevelCache {
public static void main(String[] args) {
TestOneLevelCache testOneLevelCache = new TestOneLevelCache();
testOneLevelCache.testCache1();
}
//一級快取:也就是Session級的快取(預設開啟)
public void testCache1() {
SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//查詢ID為1的使用者
User user = mapper.selectUserById(1);
System.out.println(user);
//再次查詢ID為1的物件,因為是同一個sqlSession,所以會從之前的一級快取中查詢資料
User user1 = mapper.selectUserById(1);
System.out.println(user1);
//關閉sqlSession物件
sqlSession.close();
}
}
第二次測試(執行commit操作):
import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;
/**
* @author arunner
* @date 2018/11/10
*/
public class TestOneLevelCache {
public static void main(String[] args) {
TestOneLevelCache testOneLevelCache = new TestOneLevelCache();
testOneLevelCache.testCache2();
}
public void testCache2() {
SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//查詢ID為1的使用者
User user = mapper.selectUserById(1);
System.out.println(user);
//執行delete操作
mapper.deleteUserById(5);
//commit提交
sqlSession.commit();
//再次查詢id為1的User物件,因為DML操作會清空SqlSession快取,所以會再次執行select語句
User user1 = mapper.selectUserById(1);
System.out.println(user1);
//關閉sqlSession物件
sqlSession.close();
}
}
- 測試二級快取 因為二級快取不是MyBatis的預設方式,所以需要手動配置:
- 在
mybatis-config.xml
檔案中設定快取機制:
- 在需要快取的mapper中設定:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
這裡的幾個屬性說明一下: 以上配置建立了一個LRU快取,並每隔60秒重新整理,最大儲存512個物件,而且返回的物件被認為是隻讀的。 cache元素用來開啟當前 mapper的 namespace下的二級快取。該元素的屬性設定如下: ushinterval:重新整理間隔。可以被設定為任意的正整數,而且它們代表一個合理的毫秒形式的時間段。預設情況下是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。 size:快取數目。可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的可用記憶體資源數目。預設值是1024。 readonly:只讀。屬性可以被設定為true或false。只讀的快取會給所有呼叫者返回快取物件的相同例項,因此這些物件不能被修改。這提供了很重要的效能優勢。可讀寫的快取會返回快取物件的拷貝(通過序列化)。這會慢一些,但是安全,因此預設是false。 eviction:收回策略,預設為LRU。有如下幾種:
- LRU。最近最少使用的策略,移除最長時間不被使用的物件
- FIFO。先進先出策略,按物件進入快取的順序來移除它們
- SOFT。軟引用策略,移除基於垃圾回收器狀態和軟引用規則的物件。
- WEAK。弱引用策略,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。
測試二級快取 執行刪除操作,測試二級快取:
import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;
/**
* @author arunner
* @date 2018/11/10
*/
public class TestTwoLevelCache {
public static void main(String[] args) {
TestTwoLevelCache testTwoLevelCache = new TestTwoLevelCache();
testTwoLevelCache.testCache1();
}
public void testCache1() {
// 使用工廠類獲得SqlSession物件
SqlSession session = FKSqlSessionFactory.getSqlSession();
// 獲得UserMapping物件
UserMapper mapper = session.getMapper(UserMapper.class);
// 查詢id為1的User物件,會執行select語句
User user = mapper.selectUserById(1);
System.out.println(user);
// 執行delete操作
mapper.deleteUserById(5);
// commit提交
session.commit();
// 再次查詢id為1的User物件,因為DML操作會清空SqlSession快取,所以會再次執行select語句
User user2 = mapper.selectUserById(1);
System.out.println(user2);
// 關閉SqlSession物件
session.close();
}
}
關閉SqlSession後再次查詢,測試二級快取:
import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;
/**
* @author arunner
* @date 2018/11/10
*/
public class TestTwoLevelCache {
public static void main(String[] args) {
TestTwoLevelCache testTwoLevelCache = new TestTwoLevelCache();
testTwoLevelCache.testCache2();
}
public void testCache2() {
SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//查詢ID為1的使用者
User user = mapper.selectUserById(1);
System.out.println(user);
//關閉一級快取
sqlSession.close();
//再次訪問,需要再次獲取一級快取,然後才能查詢資料,否則會丟擲異常
SqlSession sqlSession1 = FKSqlSessionFactory.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
//查詢ID為1的使用者
User user1 = mapper1.selectUserById(1);
System.out.println(user1);
//關閉sqlSession物件
sqlSession.close();
}
}
當然,如果你也可以為某個查詢設定每次都查資料庫,不從快取中獲取。設定方法如下:
useCache="false"