1. 程式人生 > >MyBatis 緩存機制

MyBatis 緩存機制

cti return AR you art resource user oot idt

前言

對於現在的java開發人員來說,MyBatis無疑是一種優秀的持久化框架,它可以使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄,是用來操作數據庫的一把利器,本節想跟大家分享學習一下MyBatis的緩存機制,提供了一級、二級緩存來緩存數據,以提高查詢的性能。

一、一級緩存

  MyBatis默認開啟一級緩存,一級緩存是SqlSession級別的緩存,意思就是,同一個SqlSession多次調用同一個Mapper中的同一個方法(即執行相同的SQL語句)只會進行一次數據庫查詢,第一次執行完後會將數據庫中查詢到的數據寫到緩存,第二次直接從緩存中取數據並不會進行第二次數據庫查詢。當SqlSession執行其他的操作時,會清空緩存。

二、二級緩存

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

<settings>  
      <setting name="cacheEnabled" value="true"/>  
</settings>

  接著還要在Mapper.xml映射文件中開啟當前的二級緩存:

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

  二級緩存是mapper級別的緩存,多個SqlSession可以共用二級緩存,意思就是,不同的SqlSession兩次執行相同的namespace下的相同的Sql語句,第二次查詢只會查詢第一次查詢並緩存的數據,也不會去數據庫中查詢。

三、一級緩存測試

1、運行環境

  JDK1.8

  MyBatis

  Mysql5.7

  Maven

  編譯器: IntelliJ IDEA

2、項目結構

技術分享圖片

3、步驟

(1)、Maven庫

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.1.1</version>
    </dependency>

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

(2)mysql數據表

mysql> desc student;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
| age   | int(11)      | YES  |     | NULL    |                |
| sex   | varchar(255) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
4 rows in set

(3)實體類

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

    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 Integer getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ", sex=‘" + sex + ‘\‘‘ +
                ‘}‘;
    }
}

(4)創建dao層接口

public interface StudentMapper {
    public Student selectStuById(Integer id)throws Exception;
}

(5)創建mapper映射文件

<?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="com.stone.dao.StudentMapper">

    <select id="selectStuById" parameterType="int" resultType="com.stone.model.Student">
        select * from student where id=#{id}
    </select>
</mapper>

(6)創建mybatis-config.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="com.stone.model" />
    </typeAliases>
    <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/bjsxt" />
                    <property name="username" value="root" />
                    <property name="password" value="xiaokai960201" />
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="Mapper/StudentMapper.xml" />
        </mappers>
    </configuration>

(7)log4j日誌配置

#設置日誌的級別,定義日誌信息的輸出目的
log4j.rootLogger=DEBUG, A1 ,R
#定義A1的輸出目的地為控制臺
log4j.appender.A1=org.apache.log4j.ConsoleAppender
#布局為 PatternLayout 可以靈活地指定布局模式。
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
#設置輸出格式
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%c]-[%p] %m%n
#定義R的輸出目的地為文件,並且文件大小到達指定尺寸的時候產生一個新的文件
log4j.appender.R=org.apache.log4j.RollingFileAppender
#設置輸出的文件地址
log4j.appender.R.File=D:\\Test_Log4j.log
#設置文件大小偉100 kb 文件到達100時,產生一個新文件,
#MaxBackupIndex 最大記錄的文件數為1 查過一個文件刪除文件較早的。
log4j.appender.R.MaxFileSize=100KB log4j.appender.R.MaxBackupIndex=1
#以下和上面一樣
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n 

(8)OneCacheTest測試類(相同sqlsession)

public class OneCacheTest {

    public static void main(String[] args) throws Exception {
        OneCacheTest oneCacheTest=new OneCacheTest();
        oneCacheTest.test1();
    }
    public void test1() throws Exception{
        SqlSession session = SqlSessionFac.getSqlSession();
        StudentMapper studentMapper=session.getMapper(StudentMapper.class);
        Student student1=studentMapper.selectStuById(1);
        System.out.println(student1.toString());
        Student student2=studentMapper.selectStuById(1);
        System.out.println(student2.toString());
        Student student5=studentMapper.selectStuById(1);
        System.out.println(student5.toString());
}
}
結果:
2018-04-28 21:11:49 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:11:51 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1268650975.
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@4b9e13df]
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==>  Preparing: select * from student where id=? 
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}

(9)OneCacheTest測試類(不同sqlsession)

public class OneCacheTest {

    public static void main(String[] args) throws Exception {
        OneCacheTest oneCacheTest=new OneCacheTest();
        oneCacheTest.test1();
    }
    public void test1() throws Exception{
        SqlSession session1 = SqlSessionFac.getSqlSession();
        StudentMapper studentMapper1=session1.getMapper(StudentMapper.class);
        Student student1=studentMapper1.selectStuById(1);
        System.out.println(student1.toString());
     session1.close(); SqlSession session2
= SqlSessionFac.getSqlSession(); session2 = SqlSessionFac.getSqlSession(); StudentMapper studentMapper2=session2.getMapper(StudentMapper.class); Student student2=studentMapper2.selectStuById(1); System.out.println(student2.toString());
     session2.close(); } }
結果:

2018-04-28 21:20:13 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:20:13 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1268650975.
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@4b9e13df]
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}
2018-04-28 21:20:14 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:20:14 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1620303253.
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@6093dd95]
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}



(10)總結

  由上述結果可看出,同一個SqlSession多次調用同一個Mapper中的同一個方法(即執行相同的SQL語句)只會進行一次數據庫查詢,第一次執行完後會將數據庫中查詢到的數據寫到緩存,第二次直接從緩存中取數據並不會進行第二次數據庫查詢。而不同的sqlsession會進行數據庫查詢,所以一級緩存是SqlSession級別的。

四、二級緩存測試

(1)在Mybatis-config中的configuration標簽中開啟二級緩存

    <settings>
        <!--開啟二級緩存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

(2)在Mapper映射文件中 開啟當前mapper 的 namespace 下的二級緩存

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

(3)創建TwoCacheTest測試類

public class OneCacheTest {

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

        // 獲取 SqlSession 對象
        SqlSession session1 = SqlSessionFac.getSqlSession();
        StudentMapper studentMapper1 = session1.getMapper(StudentMapper.class);
        Student student = studentMapper1.selectStuById(1);
        System.out.println(student.toString());
        // 關閉
        session1.close();

        // 再次獲取 SqlSession 對象
        SqlSession session2 = SqlSessionFac.getSqlSession();
        StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class);
        Student student2 = studentMapper2.selectStuById(1);
        System.out.println(student2.toString());
        session2.close();
}
}
結果:

2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 22:15:38 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 94345706.
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}
2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 94345706 to pool.
2018-04-28 22:15:38 [org.apache.ibatis.cache.decorators.LoggingCache]-[DEBUG] Cache Hit Ratio [com.stone.dao.StudentMapper]: 0.5
Student{id=1, name=‘asd‘, age=11, sex=‘男‘}

 

(4)由結果可以看出

  開啟二級緩存後,不同sqlsession訪問同一個sql的同一個參數,第一次從數據庫中查詢後,第二次查詢就直接從緩存中取數據了,不用經過數據庫。

  

MyBatis 緩存機制