MyBatis 缓存机制

Posted StoneGeek

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis 缓存机制相关的知识,希望对你有一定的参考价值。

前言

对于现在的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 缓存机制的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis缓存机制

MyBatis的缓存机制

聊聊MyBatis缓存机制

Mybatis缓存机制

一次读懂mybatis中的缓存机制

推荐学java——MyBatis高级