# 一、简答

Mybatis 的缓存有 一级缓存二级缓存

# 一级缓存

一级缓存在 Mybatis 中默认是开启并生效的。

一级缓存存在两种作用范围:

  • SESSION(默认) 在同一个 SqlSession 中多次执行同一个查询,除第一次走数据库,剩下的都走缓存
  • STATEMENT 每执行完一个 Mapper 中的语句后都会将一级缓存清除。

# 二级缓存

二级缓存在 Mybatis 中默认是不开启。准确的来讲应该是二级缓存的全局配置开关是默认开启的但是想要二级缓存生效,还需要进行配置。

二级缓存的作用范围是同一个 namespace 下的mapper 映射文件内容。多个 SqlSession 之间可以共享缓存内容。

# spring 整合 Mybatis 后

Spring 整合 Mybatis 之后,二级缓存照常生效,但是一级缓存有了改变。如果不是在同一个事务中每一次 Mapper 方法的调用,都会生成一个新的 Sqlsession,这时候一级缓存就不会命中。

# 总结

默认情况下,相关信息如下:

作用域 默认是否开启
一级缓存 SESSION(默认) 和 STATEMENT 开启
二级缓存 同一个namespace下的mapper映射文件内容 关闭

# 二、代码实践

# 验证一级缓存的存在

# 验证作用范围 STATEMENT

当 一级缓存 的作用域改为 STATEMENT 时,每执行一次 Mapper 中的语句后会将一级缓存清除。

此时一级缓存相当于摆设。

step1、设置一级缓存作用范围为 STATEMENT(mybatis-config.xml中)

<settings>
  <!-- 调整一级缓存作用域为 STATEMENT -->
  <setting name="localCacheScope" value="STATEMENT"/>
</settings>

step2、相关代码

/**
 * <p> Mybatis 缓存验证测试 </p>
 *
 * @Author WTF名字好难取
 */
public class DemoTestFromXML {
    public static SqlSession getSqlSession() throws FileNotFoundException {
        //配置文件
        InputStream configFile = new FileInputStream("xxx.mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configFile);
        //加载配置文件得到SqlSessionFactory
        return sqlSessionFactory.openSession();
    }

    public static void main(String[] args) throws Exception {
        // 一级缓存:STATMENT 验证
        oneLeveCacheVerifySTATEMENT();
    }

    /**
     * <p> 一级缓存:STATMENT验证 </p>
     *
     * @throws Exception
     */
    public static void oneLeveCacheVerifySTATEMENT() throws Exception{
        UserInfoMapper mapper = getSqlSession().getMapper(UserInfoMapper.class);
        // 第一次查询
        List<UserInfo> userInfos1 = mapper.selectList();
        // 第二次查询
        List<UserInfo> userInfos2 = mapper.selectList();
    }
}

step3、控制台打印信息

[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1188e820]
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - ==>  Preparing: SELECT * FROM user_info 
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - ==> Parameters: 
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - <==      Total: 1
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - ==>  Preparing: SELECT * FROM user_info 
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - ==> Parameters: 
[main] DEBUG c.q.m.m.UserInfoMapper.selectList - <==      Total: 1

从控制台打印的 SQL 能够知道,查询语句执行了两次。

一级缓存作用范围为STATEMENT时,每一次mapper 接口的调用,缓存都会被刷新,相当于缓存没什么卵用

# 验证作用范围 SESSION

当 一级缓存 的作用范围是 SESSION 时,再同一个 SESSION 中,多次进行同一个查询,只有第一次查询会执行SQL语句,其他相同的SQL查询,走的是一级缓存。

step1、设置一级缓存作用范围为 SESSION (mybatis-config.xml中)

一级缓存 默认作用范围为 SESSION ,可以不设置

<settings>
  <!-- 调整一级缓存作用域为 SESSION -->
  <setting name="localCacheScope" value="SESSION"/>
</settings>

step2、相关代码

/**
 * <p> Mybatis 缓存验证测试 </p>
 *
 * @Author WTF名字好难取
 */
public class DemoTestFromXML {
    public static SqlSession getSqlSession() throws FileNotFoundException {
        //配置文件
        InputStream configFile = new FileInputStream("xxx.mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configFile);
        //加载配置文件得到SqlSessionFactory
        return sqlSessionFactory.openSession();
    }

    public static void main(String[] args) throws Exception {
        // 一级缓存:SESSION 验证
        oneLeveCacheVerifySESSION();
    }

    /**
     * <p> 一级缓存:SESSION验证 </p>
     *
     * @throws Exception
     */
    private static void oneLeveCacheVerifySESSION() throws Exception{
        /** 方式一:同一个 SqlSession **/
        UserInfoMapper mapper = getSqlSession().getMapper(UserInfoMapper.class);
        // 第一次查询
        List<UserInfo> way1_info1 = mapper.selectList();
        // 第二次查询
        List<UserInfo> way1_info2 = mapper.selectList();

        System.out.println("=====================");

        /** 方式二:不同的 SqlSession **/
        UserInfoMapper mapper1 = getSqlSession().getMapper(UserInfoMapper.class);
        UserInfoMapper mapper2 = getSqlSession().getMapper(UserInfoMapper.class);
        List<UserInfo> way2_info1 = mapper1.selectList();
        List<UserInfo> way2_info2 = mapper2.selectList();
    }
}

step3、控制台打印信息

[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
[main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 789219251.
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f0a87b3]
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==>  Preparing: select id, name from user_info where id = ? 
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==> Parameters: 1(Integer)
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - <==      Total: 1
=====================
[main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 1686369710.
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6483f5ae]
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==>  Preparing: select id, name from user_info where id = ? 
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==> Parameters: 1(Integer)
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - <==      Total: 1
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
[main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 134310351.
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@80169cf]
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==>  Preparing: select id, name from user_info where id = ? 
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==> Parameters: 1(Integer)
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - <==      Total: 1

从控制台打印能够得知: 在 SESSION 范围,同一 SqlSession 执行多次相同sql语句,除第一次外,其余都会直接命中一级缓存。 不同 SqlSession 中执行的相同 sql 不会命中缓存。

# 验证二级缓存的存在

小贴士: 1、缓存只作用于 cache 标签所在的映射文件中的语句。 2、如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。 话句话说:注解和XML不能同时存在,只能选一种,不然二级缓存不生效。 3、二级缓存要命中,需要先开启一个 sql 语句,然后关闭该 session ,在建立一个新的 session 还行相同的 sql 此时二级缓存才会命中。

step1、首先需要确认全局开关是否开启 默认是开启的。显示的配置如下:

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

step2、配置开启二级缓存

在需要开启的 Mapper.xml 文件中追加 <cache>

<mapper namespace="xxx.UserInfoMapper">
  ......
  <!-- 二级缓存开启 -->
    <cache/>
  ......
</mapper>

step3、相关代码

/**
 * <p> Mybatis 二级缓存验证测试 </p>
 *
 * @Author WTF名字好难取
 */
public class TwoLeveCacheDemoTestFromXML {
    public static SqlSessionFactory getSqlSessionFactory() throws FileNotFoundException {
        //配置文件
        InputStream configFile = new FileInputStream("xxx.mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configFile);
        //加载配置文件得到SqlSessionFactory
        return sqlSessionFactory;
    }

    public static void main(String[] args) throws Exception {
        // 二级缓存 验证
        twoLeveCacheVerify();

    }

    /**
     * <p> 二级缓存 验证 </p>
     *
     * @throws Exception
     */
    private static void twoLeveCacheVerify() throws Exception{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        /** 不同的 SqlSession **/
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        UserInfoMapper mapper1 = sqlSession1.getMapper(UserInfoMapper.class);
        UserInfo info1 = mapper1.selectByPrimaryKey(1);
        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        UserInfoMapper mapper2 = sqlSession2.getMapper(UserInfoMapper.class);
        UserInfo info2 = mapper2.selectByPrimaryKey(1);
        sqlSession2.close();
    }
}

step4、控制台打印信息

[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
[main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 1375995437.
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5204062d]
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==>  Preparing: select id, name from user_info where id = ? 
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - ==> Parameters: 1(Integer)
[main] DEBUG c.q.m.m.U.selectByPrimaryKey - <==      Total: 1
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5204062d]
[main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5204062d]
[main] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1375995437 to pool.
[main] DEBUG c.q.mybatis.mapper.UserInfoMapper - Cache Hit Ratio [com.qguofeng.mybatis.mapper.UserInfoMapper]: 0.5

从控制台打印内容,验证了二级缓存的作用范围是同一个 namespace 下的mapper 映射文件内容,多个 SqlSession 之间可以共享缓存内容。

# Spring整合Mybatis 之后,一级缓存的验证

step1、进行 spring 和 Mybatis 的整合(省略) step2、相关测试代码

/**
 * <p> 缓存单元测试 </p>
 *
 * @Author WTF名字好难取
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class CacheTest {
    @Autowired
    private UserInfoMapper userInfoMapper;

    /**
     * <p> 缓存测试 </p>
     * 二级缓存照常生效
     */
    @Test
    public void cacheTest() throws Exception{
        UserInfo userInfo1 = userInfoMapper.selectByPrimaryKey(1);
        UserInfo userInfo2 = userInfoMapper.selectByPrimaryKey(1);

        System.in.read();
    }


    /**
     * <p> 缓存测试 </p>
     * spring 整合 mybatis 之后 : 每一次 sql 查询都会生成一个 Sqlsession 实例 。
     * 只有在同一个事务中,才会是同一个 SqlSession ,一级缓存才会生效。
     */
    @Test
    @Transactional
    public void cacheTestTransaction() throws Exception{
        UserInfo userInfo1 = userInfoMapper.selectByPrimaryKey(1);
        UserInfo userInfo2 = userInfoMapper.selectByPrimaryKey(1);

        System.in.read();
    }
}

step3、控制台打印 spring整合Mybatis一级缓存验证_控制台信息

从控制台信息得到,非同一事务每一次 mapper 调用都是新的SqlSession 实例。在同一事务中,一级缓存才会命中

精彩内容推送,请关注公众号!
最近更新时间: 3/24/2020, 9:44:42 PM