Mybatis 系列9-缓存

像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。
Mybatis 中缓存分为一级缓存,二级缓存。
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

一级缓存

为了看的更清楚,重新创建个maven工程
1、pom文件:

<packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2、resource创建log4j.properties、jdbcConfig.properties、SqlMapConfig.xml

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2
jdbc.username=root
jdbc.password=123456
<?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>
    <!-- 配置properties-->
    <properties resource="jdbcConfig.properties"></properties>

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

    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.mantishell.domain"></package>
    </typeAliases>

    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>

            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!-- 配置映射文件的位置 -->
    <mappers>
        <package name="com.mantishell.dao"></package>
    </mappers>
</configuration>

3、创建持久层的配置文件com/mantishell.dao/IUserDao.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="com.mantishell.dao.IUserDao">
    <!--开启二级缓存-->
    <!--<cache />-->

    <!-- 查询所有 -->
    <select id="findAll" resultType="user">
        select * from user
    </select>

    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="user" useCache="true">
        select * from user where id = #{uid}
    </select>

    <!-- 更新用户信息-->
    <update id="updateUser" parameterType="user">
        update user set username=#{username},address=#{address} where id=#{id}
    </update>
</mapper>

4、创建实体

package com.mantishell.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

5、持久层接口:

package com.mantishell.dao;

import com.mantishell.domain.User;

import java.util.List;

public interface IUserDao {
    /**
     * 查询所有用户,同时获取到用户下所有账户的信息
     * @return
     */
    List<User> findAll();


    /**
     * 根据id查询用户信息
     * @param userId
     * @return
     */
    User findById(Integer userId);

    /**
     * 更新用户信息
     * @param user
     */
    void updateUser(User user);
}

6、测试

/**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);

        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);
    }

结果是:

com.mantishell.domain.User@282003e1
2020-03-17 22:16:36,821 554    [           main] DEBUG    com.mantishell.dao.IUserDao  - Cache Hit Ratio [com.mantishell.dao.IUserDao]: 0.0
com.mantishell.domain.User@282003e1
true

可以看出第二次查询时并没有去数据库中查找,而是从缓存里取的。

当把sqlSession关闭掉或清空缓存:

@Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);

        sqlSession.close();
        //再次获取SqlSession对象
        sqlSession = factory.openSession();

        //sqlSession.clearCache();//此方法也可以清空缓存

        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);
    }

结果是:

com.mantishell.domain.User@6e2c9341
2020-03-17 22:26:28,239 619    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@543e710e]
2020-03-17 22:26:28,239 619    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 1413378318 to pool.
2020-03-17 22:26:28,239 619    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
2020-03-17 22:26:28,240 620    [           main] DEBUG source.pooled.PooledDataSource  - Checked out connection 1413378318 from pool.
2020-03-17 22:26:28,240 620    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@543e710e]
2020-03-17 22:26:28,242 622    [           main] DEBUG ntishell.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ? 
2020-03-17 22:26:28,242 622    [           main] DEBUG ntishell.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
2020-03-17 22:26:28,247 627    [           main] DEBUG ntishell.dao.IUserDao.findById  - <==      Total: 1
com.mantishell.domain.User@4e4aea35
false

显然从数据库查询了两次。

二级缓存

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个 Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存需要手动开启
sqlSession1查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
此时如果SqlSession3去执行相同mapper映射下的sql语句,执行commit提交,将会清空该mapper映射下的二级缓存区的数据。
SqlSession2去查询与SqlSession1相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存的开启和关闭

1、在SqlMapConfig.xml 文件开启二级缓存

    <!--开启二级缓存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

cacheEnabled的取值默认是true,所以这一步可以省略。true-表示开启;false-表示关闭

2、持久层配置文件修改

<?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="com.mantishell.dao.IUserDao">
    <!--开启二级缓存-->
    <cache />

    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="user" useCache="true">
        select * from user where id = #{uid}
    </select>

<select>标签中设置useCache=”true”代表当前这个statement要使用二级缓存,如果不使用二级缓存可以设置为false。
注意:如果每次查询都需要最新的数据,那么需要设置为useCache=false,禁用二级缓存。

测试:

    @Test
    public void testFirstLevelCache(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        sqlSession1.close();//一级缓存消失

        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();

        System.out.println(user1 == user2);
    }

首先关闭了一级缓存,免得影响后面结果。从结果可以看出,两次查询中sql只执行一次,也就是说是从二级缓存中取的值

2020-03-17 22:54:17,508 287    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
2020-03-17 22:54:17,904 683    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 392781299.
2020-03-17 22:54:17,904 683    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@17695df3]
2020-03-17 22:54:17,912 691    [           main] DEBUG ntishell.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ? 
2020-03-17 22:54:17,934 713    [           main] DEBUG ntishell.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
2020-03-17 22:54:17,959 738    [           main] DEBUG ntishell.dao.IUserDao.findById  - <==      Total: 1
com.mantishell.domain.User@282003e1
2020-03-17 22:54:17,967 746    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@17695df3]
2020-03-17 22:54:17,968 747    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@17695df3]
2020-03-17 22:54:17,968 747    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 392781299 to pool.
2020-03-17 22:54:17,972 751    [           main] DEBUG    com.mantishell.dao.IUserDao  - Cache Hit Ratio [com.mantishell.dao.IUserDao]: 0.5
com.mantishell.domain.User@5606c0b
posted @ 2020-03-18 20:59  mantishell  阅读(207)  评论(0编辑  收藏  举报