← 返回首页
Mybatis基础教程(十六)
发表时间:2022-03-11 15:53:59
Mybatis的二级缓存

1.二级缓存

我们知道对于Mybatis的一级缓存,如果两个SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次。为了解决这个问题Mybatis提供了二级缓存,与一级缓存比较,二级缓存的存储范围更大,多个SqlSession可以共享二级缓存,而且二级缓存可以自己定义要缓存的资源。

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着:

以上是二级缓存在默认状态下的特征,如果需要调整上述特性,可以通过<cache>元素属性来实现。

Cache标签有六个属性,其中: |属性名|含义| |-|-| |type|指定缓存(cache)接口的实现类型,当需要和ehcache整合时更改该参数值即可。| |flushInterval|刷新间隔。可被设置为任意的正整数,单位毫秒。默认不设置。| |size|引用数目。可被设置为任意正整数,缓存的对象数目等于运行环境的可用内存资源数目。默认是1024。| |readOnly|只读,true或false。只读的缓存会给所有的调用者返回缓存对象的相同实例。默认是false。| |eviction|缓存收回策略。LRU(最近最少使用的),FIFO(先进先出),SOFT( 软引用),WEAK( 弱引用)。默认是 LRU。|

在 Mapper 中,在select中可设置useCache="false"来禁用缓存,默认是开启的;在insert、update、delete中设置flushCache="true"来清空缓存(刷新缓存),默认是会清空缓存。

实例

在上小节案例基础上,测试mybatis二级缓存。

1.改写Users.java 实现串行化接口。

//用户实体类,对应我们users表。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users implements Serializable {

    private Integer uid; //用户编号
    private String username; //用户名
    private String password; //密码
    private String mobile; //手机号码
    private String email; //电子邮箱
    private String type; //用户类型

}

2.在mybatis-config.xml中开启二级缓存

<settings>
   <!--设置启用数据库字段下划线映射到java对象的驼峰式命名属性,默认为false-->
   <!-- 配置打印 SQL log 的路径前缀 以及懒加载 -->
   <setting name="logPrefix" value="mybatis.sql."/>
   <!-- 打开延迟加载的全局开关 -->
   <setting name="lazyLoadingEnabled" value="true"/>
   <setting name="aggressiveLazyLoading" value="false"/>
   <setting name="mapUnderscoreToCamelCase" value="true"/>
   <!--开启二级缓存-->
   <setting name="cacheEnabled" value="true"/>
</settings>

3.在usersMapper.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.mapper.usersMapper">
    <!-- 开启本Mapper下的 namespace下的二级缓存 -->
    <cache></cache>
    <!--查询所有用户-->
    <select id="getAllUsers" resultType="Users">
    select * from users
    </select>

    <!--根据用户编号查询某个用户资料-->
    <select id="getUser" parameterType="int" resultType="Users" useCache="true">
     select * from users where uid=#{uid}
    </select>

    <!--新增用户-->
    <insert id="addUser" parameterType="Users" useGeneratedKeys="true" keyProperty="uid">
        insert into users (username,password) values (#{username},#{password})
    </insert>

    <!--更新用户-->
    <update id="updateUser" parameterType="Users">
        update users set username = #{username},password=#{password} where uid = #{uid}
    </update>

    <!--删除用户-->
    <delete id="deleteUser" parameterType="int">
        delete from users where uid = #{uid}
    </delete>
</mapper>

4.编写测试类

    @Test
    public void testSecnodLevelCache(){
        SqlSession session = null;
        Users u = null;
        try {
            session = SqlSessionFactoryUtil.openSqlSession();
            u = session.selectOne("getUser", 3);
            System.out.println(u);
            session.close();
            session=null;
            //打开一个新的SqlSession
            session = SqlSessionFactoryUtil.openSqlSession();
            u = session.selectOne("getUser", 3);
            System.out.println(u);

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

运行结果:

15:49:27.884 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==>  Preparing: select * from users where uid=?
15:49:27.924 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==> Parameters: 3(Integer)
15:49:27.974 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - <==      Total: 1
Users(uid=3, username=宝宝, password=123456, mobile=15991167345, email=403353606@qq.com, type=admin)
15:49:27.983 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:49:27.983 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:49:27.984 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 68857875 to pool.
15:49:27.985 [main] WARN org.apache.ibatis.io.SerialFilterChecker - As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
15:49:27.987 [main] DEBUG com.mapper.usersMapper - Cache Hit Ratio [com.mapper.usersMapper]: 0.5
Users(uid=3, username=宝宝, password=123456, mobile=15991167345, email=403353606@qq.com, type=admin)

我们发现两个不同的SqlSession执行相同的查询语句,仅仅发送了一次select查询语句。说明第二次查询使用了二级缓存。

执行更新语句后会刷新二级缓存。

    @Test
    public void testSecnodLevelCache(){
        SqlSession session = null;
        Users u = null;
        try {
            session = SqlSessionFactoryUtil.openSqlSession();
            u = session.selectOne("getUser", 3);
            System.out.println(u);
            //执行更新操作
            u.setPassword("123456");
            session.update("updateUser",u);
            session.commit();
            session.close();

            session = SqlSessionFactoryUtil.openSqlSession();
            u = session.selectOne("getUser", 3);
            System.out.println(u);

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

运行结果:

15:52:12.552 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==>  Preparing: select * from users where uid=?
15:52:12.597 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==> Parameters: 3(Integer)
15:52:12.647 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - <==      Total: 1
Users(uid=3, username=宝宝, password=123456, mobile=15991167345, email=403353606@qq.com, type=admin)
15:52:12.650 [main] DEBUG mybatis.sql.com.mapper.usersMapper.updateUser - ==>  Preparing: update users set username = ?,password=? where uid = ?
15:52:12.651 [main] DEBUG mybatis.sql.com.mapper.usersMapper.updateUser - ==> Parameters: 宝宝(String), 123456(String), 3(Integer)
15:52:12.652 [main] DEBUG mybatis.sql.com.mapper.usersMapper.updateUser - <==    Updates: 1
15:52:12.652 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:52:12.653 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:52:12.653 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:52:12.653 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 68857875 to pool.
15:52:12.655 [main] WARN org.apache.ibatis.io.SerialFilterChecker - As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
15:52:12.657 [main] DEBUG com.mapper.usersMapper - Cache Hit Ratio [com.mapper.usersMapper]: 0.0
15:52:12.657 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
15:52:12.657 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Checked out connection 68857875 from pool.
15:52:12.657 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41ab013]
15:52:12.658 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==>  Preparing: select * from users where uid=?
15:52:12.658 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - ==> Parameters: 3(Integer)
15:52:12.659 [main] DEBUG mybatis.sql.com.mapper.usersMapper.getUser - <==      Total: 1
Users(uid=3, username=宝宝, password=123456, mobile=15991167345, email=403353606@qq.com, type=admin)

我们发现执行完更新操作后要刷新二级缓存,所以第二次查询又执行了一次select查询语句。