【SSM】SSM之MyBatis框架:MyBatis的緩存技術

一、緩存:

請先觀察下面程序的執行結果:

package cn.jingpengchong.userinfo.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import cn.jingpengchong.userinfo.dao.IUserInfoDao;
import cn.jingpengchong.userinfo.vo.UserInfo;

public class Test {
	public static void main(String[] args) {
		try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
			SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession session = factory.openSession();
			IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
			List<UserInfo> list = userInfoDao.selectAll();
			System.out.println(list.size());

			list = userInfoDao.selectAll();
			System.out.println(list.size());
			
			session.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

運行結果如下:
在這裏插入圖片描述
我們發現明明我們程序中查了兩次數據庫,但從日誌輸出來看實際上只訪問了一次數據庫,那麼這是什麼原因呢?答案就是:緩存。爲了提升查詢效率,提高用戶體驗,MyBatis提供了數據緩存支持,依據數據緩存的有效範圍默認定義了一級緩存和二級緩存。第一次查詢數據庫後,Mybatis自動將結果放在了一級緩存中,當第二次的查詢語句與第一次一樣的時候,並且中間沒有操作數據庫中的數據且沒有清除緩存時,Mybatis就會直接把緩存中的數據返回給用戶。這樣做的好處就是減少了對數據庫的頻繁訪問對數據庫造成的損害,並且提升了查詢速度。

二、一級緩存:

一級緩存爲SqlSession級別的緩存,也稱爲本地緩存,默認是開啓的,不能關閉。但是以下4種情況將會再次訪問數據庫:

1、在不同的SqlSession中查詢數據:

這種情況嚴格意義上來講並不算是一級緩存失效了,因爲一級緩存是SqlSession級別的緩存,在該SqlSession外自然也就訪問不到它的緩存了。例如,將上面程序中的try代碼塊中的代碼做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

此時如果再次運行程序,就會訪問兩次數據庫了:
在這裏插入圖片描述

2、相同SqlSession中查詢數據,但查詢條件不同:

這種情況也不算是緩存失效了,緩存中的數據還在,只不過由於緩存中沒有存儲該語句查詢出來的數據,因此第二次Mybatis還會去訪問數據庫。例如,將上面程序中的try代碼塊中的代碼做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll(20);
System.out.println(list.size());

list = userInfoDao.selectAll(25);
System.out.println(list.size());

session.close();

此時如果再次運行程序,就會訪問兩次數據庫了:
在這裏插入圖片描述

3、相同SqlSession中查詢數據,但兩次查詢之間執行了增刪改操作:

這種情況是緩存失效了,因爲增刪改都有一個flushCache屬性並且默認值是true,如果執行了增刪改操作就會刷新緩存。當然,如果是false的話就不會刷新緩存了,但是這種做法是錯誤的,會導致第二次查詢到的數據不正確。例如,將上面程序中的try代碼塊中的代碼做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());

userInfoDao.delete("'%四%'");

list = userInfoDao.selectAll();
System.out.println(list.size());

session.close();

此時如果再次運行程序,就會訪問兩次數據庫了:
在這裏插入圖片描述

4、相同SqlSession中查詢數據,但第二次查詢前,程序調用SqlSession對象clearCache()方法手動清除了一級緩存:

既然清除了緩存,那麼之前的緩存當然是失效了。例如,將上面程序中的try代碼塊中的代碼做如下修改:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());

session.clearCache();

list = userInfoDao.selectAll();
System.out.println(list.size());

session.close();

此時如果再次運行程序,就會訪問兩次數據庫了:
在這裏插入圖片描述

三、二級緩存:

該級緩存爲namespace級別的緩存,通過SqlSession查詢數據,這些數據將會放到當前會話的一級緩存中;如果當前會話關閉,則一級緩存中的數據會被保存到二級緩存中,此後新的SqlSession將從二級緩存中查找數據。
二級緩存默認不開啓,但如果使用二級緩存需要在每個XML映射文件中添加<cache></cache>以配置該級緩存,二級緩存就可以通過在全局配置文件配置setting標籤來關閉該級緩存:

  • cache標籤屬性:
  • eviction:緩存回收策略:
    • LRU – 最近最少使用的:移除最長時間不被使用的對象,默認值
    • FIFO – 先進先出:按對象進入緩存的順序來移除它們。
    • SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
    • WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
  • flushInterval:刷新間隔,單位毫秒,默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新
  • size:引用數目,正整數,代表緩存最多可以存儲多少個對象,太大容易導致內存溢出
  • readOnly:只讀,默認爲false。true:只讀緩存;會給所有調用者返回緩存對象的相同實例,速度快;false:讀寫緩存;會返回緩存對象的拷貝(通過序列化),速度慢但安全。
    並且二級緩存存儲的數據必須可序列化,如果是自定義類型的話,要實現Serializable接口。
    例如,在XML映射文件中添加<cache></cache>後,將上面程序中的try代碼塊中的代碼做如下修改:
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

運行程序,會發現儘管兩次查詢不在同一個SqlSession中,依然只訪問一次數據庫,並且我們可以從日誌中清楚的看到第二次查詢是從緩存中獲取的:
在這裏插入圖片描述
但是如果兩次查詢之間做了增刪改操作,那麼同樣會使得緩存中的數據被清空:

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
IUserInfoDao userInfoDao = session.getMapper(IUserInfoDao.class);
List<UserInfo> list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

session = factory.openSession();
userInfoDao = session.getMapper(IUserInfoDao.class);
userInfoDao.delete("'%四%'");
list = userInfoDao.selectAll();
System.out.println(list.size());
session.close();

運行結果如下:
在這裏插入圖片描述
從中我們看到了Mybatis緩存技術的好處,同時也發現了其中存在的問題,比如,我們僅僅修改數據庫中的一條數據,它就會把緩存給清空了,其他查詢就只能再次訪問數據庫了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章