1. 程式人生 > >MyBatis一級快取和二級快取

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的快取機制是基於id進行快取的,也就是說, Mybatis使用 HashMap快取資料時,是使用物件的id作為key,而物件作為 value儲存的。

例項

  • 測試一級快取 一級快取時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的預設方式,所以需要手動配置:
  1. mybatis-config.xml 檔案中設定快取機制:

在這裡插入圖片描述

  1. 在需要快取的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"在這裡插入圖片描述