Mybatis入门到精通系列

08 Mybatis 缓存

2020-04-25  本文已影响0人  better_future

Mybatis 作为持久层框架也提供了缓存策略,提供缓存策略来减少数据库的查询,提高性能。


image.png

一、Mybati 一级缓存

一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。默认是开启的
测试根据id查询用户

@Test
    public void findById(){
        User user1 = userMapper.findById(41);
        System.out.println("第一次查:"+user1);
        User user2 = userMapper.findById(41);
        System.out.println("第二次查:"+user1);
        System.out.println(user1 == user2);
    }

结果

第一次查:User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京, accounts=null)
第二次查:User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京, accounts=null)
true

sql记录


image.png

因此,两个查询的对象完全相同,而且第二次的查询没有发出sql语句。
那么一级缓存会存在读取到可能已经修改的数据怎么办?(脏读)

一级缓存的分析

一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改添加删除commit()close()等方法时,就会清空一级缓存。

image.png

第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

测试清空一级缓存 使用commit
 @Test
    public void findById(){
        User user1 = userMapper.findById(41);
        System.out.println("第一次查:"+user1);
        session.commit();
        User user2 = userMapper.findById(41);
        System.out.println("第二次查:"+user1);
        System.out.println(user1 == user2);
    }

结果


image.png

发出了两次的查询,而且两次查询的返回对象都是不同的。即清空了一级缓存,而且发出了两次的sql查询。

一、Mybati 二级缓存

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

image.png
第1步:开启二级缓存

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

<settings>
<!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>
第2步:配置相关的 Mapper 映射文件
<mapper namespace="com.it.Mapper.UserMapper">
    <!-- 开启二级缓存的支持 -->
    <cache></cache>
</mapper>

<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

第3步:置 配置 statement 上面的 useCache
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>

将 UserDao.xml 映射文件中的<select>标签中设置 useCache="true"代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。避免脏读。

二级缓存测试:
那么二级缓存的测试就需要用不同的SqlSession来操作了。

@Before//在测试方法执行之前执行
    public void init()throws Exception {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.创建 SqlSession 工厂对象
        factory = builder.build(in);
        //4.创建 SqlSession 对象
        session1 = factory.openSession();
        session2 = factory.openSession();
        //5.创建 Dao 的代理对象
        userMapper1 = session1.getMapper(UserMapper.class);
        userMapper2 = session2.getMapper(UserMapper.class);
    }

测试一

@Test
    public void findById(){
        User user1 = userMapper1.findById(41);
        System.out.println("第一次查:"+user1);
        User user2 = userMapper2.findById(41);
        System.out.println("第二次查:"+user2);
        System.out.println(user1 == user2);
    }

这里session1的mapper查询完,立马使用session2去查询


image.png

可以看到发出了两次的sql查询。

测试二
先用一个session去查,查完了关闭session1(即关闭了session1一级缓存),然后再用session2去查询

@Test
    public void findById(){
        User user1 = userMapper1.findById(41);
        System.out.println("第一次查:"+user1);

        session1.close(); //将一级缓存关闭

        User user2 = userMapper2.findById(41);
        System.out.println("第二次查:"+user2);
        System.out.println(user1 == user2);
    }
image.png

只发出了一次sql查询语句,用的是session1的二级缓存
但是查询到的对象是不同的,也就是说,session2用session1中的二级缓存结果重新开辟的一个新的对象,内容相同。

二级缓存注意事项
上一篇 下一篇

猜你喜欢

热点阅读