Mybatis學習總結五之快取
MyBatis 是持久層框架,支援一級快取和二級快取
mybatis提供查詢快取,用於減輕資料壓力,提高資料庫效能。
- 一級快取: 基於PerpetualCache 的 HashMap本地快取,其儲存作用域為 Session,當 Session flush 或 close 之後,該Session中的所有 Cache 就將清空。
- 二級快取與一級快取其機制相同,預設也是採用 PerpetualCache,HashMap儲存,不同在於其儲存作用域為 Mapper(Namespace),並且可自定義儲存源,如 Ehcache。
- 對於快取資料更新機制,當某一個作用域(一級快取/二級快取)的進行了 C/U/D 操作後,預設該作用域下所有 select 中的快取將被清空。
一級快取
一級快取是SqlSession級別的快取。在操作資料庫時需要構造 sqlSession物件,在物件中有一個數據結構(HashMap)用於儲存快取資料。不同的sqlSession之間的快取資料區域(HashMap)是互相不影響的。
package com.aiit.test; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.aiit.pojo.User; public class TestMyBatisDemo { /* * 一級快取: 也就Session級的快取(預設開啟) */ public static void main(String[] args) { String resource="mybatis.cfg.xml"; InputStream inputStream = TestMyBatisDemo.class.getClassLoader().getResourceAsStream(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = factory.openSession(); //1.查詢 String statement1 = "com.aiit.dao.UserMapper.selectOne"; User user1 = session.selectOne(statement1,1); System.out.println("查詢結果1: " + user1); /* 必須是同一個Session,如果session物件已經close()過了就不可能用了 */ user1 = session.selectOne(statement1,1); System.out.println("查詢結果2: " + user1); session.close(); /** * session被關閉下面程式碼就沒有結果,需要重新執行session = factory.openSession(); * user1 = session.selectOne(statement1,1); System.out.println("查詢結果3: " + user1); */ session = factory.openSession(); user1 = session.selectOne(statement1,1); System.out.println("查詢結果4: " + user1); //3.修改 執行增刪改時都會自動清除快取,確保操作後和資料庫一致 String statement3 = "com.aiit.dao.UserMapper.updateOne"; User user3 = new User(9, "中國"); int updateResult = session.update(statement3, user3); System.out.println("修改成功"+updateResult); session.commit(); session.close(); } }
特別注意的地方:
1.一級快取: 也就Session級的快取(預設開啟)
2. 必須是同一個Session,如果session物件已經close()過了就不可能用了,需要重新session = factory.openSession();
3.一旦執行增刪改,快取將會被清除
二級快取
1.在mybatis.cfg.xml中開啟二級快取
<configuration> <settings> <!--這個配置使全域性的對映器(二級快取)啟用或禁用快取--> <setting name="cacheEnabled" value="true" /> ..... </settings> .... </configuration>
2.UserMapper.xml在對映檔案中開啟二級快取
具體配置 開啟快取cache、執行快取useCache、重新整理快取flushCache
<?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="com.aiit.dao.UserMapper">
<!--開啟本mapper的namespace下的二級快取-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
<!--可以通過設定useCache來規定這個sql是否開啟快取,ture是開啟,false是關閉-->
<select id="selectOne" parameterType="int" resultType="com.aiit.pojo.User" useCache="true">
SELECT * FROM tbl_user WHERE tbl_user.id=#{id}
</select>
<!--重新整理二級快取 flushCache
<select id="selectOne" parameterType="int" resultType="com.aiit.pojo.User" flushCache="true">
SELECT * FROM tbl_user WHERE tbl_user.id=#{id}
</select>
-->
</mapper>
3.使用二級快取時,User類必須實現一個Serializable介面===> User implements Serializable
4.測試二級快取
使用兩個不同的SqlSession物件去執行相同查詢條件的查詢,第二次查詢時不會再發送SQL語句,而是直接從快取中取出資料
package com.aiit.test;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.aiit.pojo.User;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
String resource="mybatis.cfg.xml";
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//開啟兩個不同的SqlSession
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
String statement1 = "com.aiit.dao.UserMapper.selectOne";
User user1 = session1.selectOne(statement1,1);
//一定要提交事務之後二級快取才會起作用
session1.commit();
System.out.println("查詢結果1: " + user1);
//由於使用的是兩個不同的SqlSession物件,所以即使查詢條件相同,一級快取也不會開啟使用
user1 = session2.selectOne(statement1,1);
System.out.println("查詢結果1: " + user1);
}
}
5.驗證我們可以根據兩條查詢語句的時間,檢視是否執行快取
long startTime = System.currentTimeMillis(); //獲取開始時間
User user1 = session1.selectOne(statement1,1); //測試的程式碼段
long endTime = System.currentTimeMillis(); //獲取結束時間
long time = endTime - startTime ;
System.out.println("查詢時間: " + time );
特別注意:
1. 對映語句檔案中的所有select語句將會被快取。
2. 對映語句檔案中的所有insert,update和delete語句會重新整理快取。
3. 快取會使用Least Recently Used(LRU,最近最少使用的)演算法來收回。
4. 快取會根據指定的時間間隔來重新整理。
5. 快取會儲存1024個物件
cache標籤
<cache
eviction="FIFO" <!--回收策略為先進先出-->
flushInterval="60000" <!--自動重新整理時間60s-->
size="512" <!--最多快取512個引用物件-->
readOnly="true"/> <!--只讀-->
1.eviction:代表的是快取回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一處最長時間不用的物件
(2) FIFO,先進先出,按物件進入快取的順序來移除他們
(3) SOFT,軟引用,移除基於垃圾回收器狀態和軟引用規則的物件
(4) WEAK,弱引用,更積極的移除基於垃圾收集器狀態和弱引用規則的物件。這裡採用的是LRU,移除最長時間不用的對形象
2. flushInterval:重新整理間隔時間,單位為毫秒,這裡配置的是100秒重新整理,如果你不配置它,那麼當SQL被執行的時候才會去重新整理快取。
3.size:引用數目,一個正整數,代表快取最多可以儲存多少個物件,不宜設定過大。設定過大會導致記憶體溢位。
4. readOnly:只讀,意味著快取資料只能讀取而不能修改,這樣設定的好處是我們可以快速讀取快取,缺點是我們沒有辦法修改快取,他的預設值是false,不允許我們修改。