MyBatis 緩存機制

MyBatis 提供了查詢緩存來緩存數據,以提高查詢的性能。MyBatis 的緩存分爲一級緩存二級緩存

  • 一級緩存是 SqlSession 級別的緩存
  • 二級緩存是 mapper 級別的緩存,多個 SqlSession 共享

這裏寫圖片描述

一級緩存

一級緩存是 SqlSession 級別的緩存,是基於 HashMap 的本地緩存。不同的 SqlSession 之間的緩存數據區域互不影響。

一級緩存的作用域是 SqlSession 範圍,當同一個 SqlSession 執行兩次相同的 sql 語句時,第一次執行完後會將數據庫中查詢的數據寫到緩存,第二次查詢時直接從緩存獲取不用去數據庫查詢。當 SqlSession 執行 insert、update、delete 操做並提交到數據庫時,會清空緩存,保證緩存中的信息是最新的。

MyBatis 默認開啓一級緩存。

二級緩存

二級緩存是 mapper 級別的緩存,同樣是基於 HashMap 進行存儲,多個 SqlSession 可以共用二級緩存,其作用域是 mapper 的同一個 namespace。不同的 SqlSession 兩次執行相同的 namespace 下的 sql 語句,會執行相同的 sql,第二次查詢只會查詢第一次查詢時讀取數據庫後寫到緩存的數據,不會再去數據庫查詢。

MyBatis 默認沒有開啓二級緩存,開啓只需在配置文件中寫入如下代碼:

<settings>  
      <setting name="cacheEnabled" value="true"/>  
</settings>
  • 1
  • 2
  • 3

緩存測試

#### 環境 
- JDK1.7 
- MySQL 5.5.50 
- mybatis 3.3.0

注:MyBatis 下載地址https://github.com/mybatis/mybatis-3/releases

需要的 jar 包

  • MyBatis jar 包: mybatis-3.3.0.jar
  • MySQL 驅動 jar 包: mysql-connector-java-xxx.jar
  • 日誌記錄 jar 包: log4j-xxx.jar

1.數據庫準備

登錄 MySQL 後新建一個數據庫並取名 mybatis ,創建表 user ,並插入數據:

mysql> create table user(
    -> id int primary key auto_increment,
    -> name varchar(20),
    -> sex varchar(10),
    -> age int);

insert into user(name,sex,age) values('Tom','male',20),('Jack','male',19),('Rose','female',18),('Lucy','female',19);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.新建工程項目

(1)首先在 Eclipse 裏新建一個動態 Web 工程(Dynamic Web Project),命名爲 CacheTest 。 
(2)將前面提到的三個 jar 包拷貝到工程的/WebContent/WEB-INF/lib/ 目錄下,如下: 
這裏寫圖片描述

3.實體類

在 Java Resources/src 的包 gler.cache.model 下新建類 User.Java,一個用戶具有 id、name、sex、age 屬性。

User.java 的代碼如下:

package gler.cache.model;

public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString(){
        return id+" "+name+" "+sex+" "+age;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

4.創建方法接口和定義映射文件

新建包 gler.cache.mapper ,並在包下新建方法接口 UserMapper.java,代碼如下:

package gler.cache.mapper;

import gler.cache.model.User;

public interface UserMapper {
    // 刪除用戶
    public int deleteUser(Integer id) throws Exception;

    // 根據id查詢用戶信息
    public User selectUserById(Integer id) throws Exception;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

同樣在包下新建映射文件 UserMapper.xml,代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="gler.cache.mapper.UserMapper">
    <delete id="deleteUser" parameterType="int">
        delete from user where
        id=#{id}
    </delete>

    <select id="selectUserById" parameterType="int" resultType="User">
        select * from user where id=#{id}
    </select>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.配置文件 mybatis.cfg.xml

在目錄 Java Resources/src 下新建 MyBatis 配置文件 mybatis.cfg.xml,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>    
    <typeAliases>
        <package name="gler.cache.model" />
    </typeAliases> 
    <!-- 配置mybatis運行環境 -->
    <environments default="development">
        <environment id="development">
           <!-- type="JDBC" 代表直接使用 JDBC 的提交和回滾設置 -->
            <transactionManager type="JDBC" />

            <!-- POOLED 表示支持JDBC數據源連接池 -->
            <!-- 數據庫連接池,由 Mybatis 管理,數據庫名是 mybatis,MySQL 用戶名 root,密碼爲 root -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments> 
    <mappers>
        <!-- 通過 mapper 接口包加載整個包的映射文件 -->
        <package name="gler/cache/mapper" />
    </mappers>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

6.日誌記錄 log4j.properties

在項目目錄 Java Resources/src 下新建 MyBatis 日誌記錄文件 log4j.properties,在裏面添加如下內容:

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7.緩存測試

(1)創建一個獲取 SqlSession 的輔助類 SqlSessionFac

在 Java Resources/src 的包 gler.cache.factory 下新建輔助類 SqlSessionFac,代碼如下:

package gler.cache.factory;

import java.io.IOException;
import java.io.InputStream;

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

public class SqlSessionFac {
    private static SqlSessionFactory sqlSessionFactory = null;
    static{
        String resource = "mybatis.cfg.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

(2)一級緩存測試程序 
在 Java Resources/src 的包 gler.cache.test 下新建一級緩存測試類 OneCacheTest,代碼如下:

package gler.cache.test;

import gler.cache.factory.SqlSessionFac;
import gler.cache.mapper.UserMapper;
import gler.cache.model.User;

import org.apache.ibatis.session.SqlSession;

public class OneCacheTest {
    public static void main(String[] args) throws Exception {
        OneCacheTest ot = new OneCacheTest();
        ot.test1();
        //ot.test2();
    }

    // 普通測試
    public void test1() throws Exception{
        SqlSession session = SqlSessionFac.getSqlSession();
        // 第一次查詢
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 第二次查詢
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }

    // 有delete操作和commit提交的測試
    public void test2() throws Exception{
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // 第一次查詢
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // delete 和 commit
        userMapper.deleteUser(3);
        session.commit();
        // 第二次查詢
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

結果:

(1)運行 tes1() 方法

這裏寫圖片描述

可以看到只有第一次查詢時訪問了數據庫,第二次查詢直接從緩存讀取數據。

在這裏我們將 test1() 中的代碼改爲下面的代碼:

    public void test1() throws Exception{
        // 獲取 SqlSession 對象
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 關閉
        session.close();
        // 再次獲取 SqlSession 對象
        session = SqlSessionFac.getSqlSession();
        userMapper = session.getMapper(UserMapper.class);
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這裏寫圖片描述

同樣,由於是不同的 SqlSession,它們之間的緩存數據區域互不影響,因此兩次均訪問了數據庫。

(2)運行test2() 方法

這裏寫圖片描述

可以看到兩次查詢均訪問了數據庫,那是因爲有 delete 操作,並把操作提交到了數據庫,MyBatis 清空了緩存。

(3)二級緩存測試程序

首先我們先在配置文件中開啓二級緩存

注:settings 元素在 configuration 標籤元素中書寫順序排在第二位

<settings>  
      <setting name="cacheEnabled" value="true"/>  
</settings>
  • 1
  • 2
  • 3

接着在 UserMapper.xml 映射文件中開啓當前 mapper 的 namespace 下的二級緩存

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"></cache>
  • 1

注:cache 元素書寫順序排在第一位

在 Java Resources/src 的包 gler.cache.test 下新建一級緩存測試類 TwoCacheTest,代碼如下:

package gler.cache.test;

import gler.cache.factory.SqlSessionFac;
import gler.cache.mapper.UserMapper;
import gler.cache.model.User;

import org.apache.ibatis.session.SqlSession;

public class TwoCacheTest {
    public static void main(String[] args) throws Exception {
        TwoCacheTest tt = new TwoCacheTest();
        tt.test();
    }

    public void test() throws Exception{
        // 獲取 SqlSession 對象
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 關閉
        session.close();

        // 再次獲取 SqlSession 對象
        session = SqlSessionFac.getSqlSession();
        userMapper = session.getMapper(UserMapper.class);
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

這裏寫圖片描述

可以看到只有第一次查詢時訪問了數據庫,第二次查詢直接從二級緩存讀取數據。

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