Mybatis学习----Mybatis缓存设置

1.一级缓存

首先看一段代码,猜测一下对应的结果

 @Test
    public void testL1Cache(){
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        SysUser user1 = null;
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectById 方法,查询 id = 1 的用户
            user1 = userMapper.selectById(1l);
            System.out.println(user1);
            //对当前获取的对象重新赋值
            user1.setUserName("New Name");
            //再次查询获取 id 相同的用户
            SysUser user2 = userMapper.selectById(1l);
            System.out.println(user2);
            //虽然我们没有更新数据库,但是这个用户名和我们 user1 重新赋值的名字相同了
            Assert.assertEquals("New Name", user2.getUserName());
            System.out.println(user1==user2);
            //不仅如此,user2 和 user1 完全就是同一个实例
            Assert.assertEquals(user1, user2);

        } finally {
            //关闭当前的 sqlSession
            sqlSession.close();
        }
        System.out.println("开启新的 sqlSession");
        //开始另一个新的 session
        sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectById 方法,查询 id = 1 的用户
            SysUser user2 = userMapper.selectById(1l);
            System.out.println(user2);
            //第二个 session 获取的用户名仍然是 admin
            Assert.assertNotEquals("New Name", user2.getUserName());
            //这里的 user2 和 前一个 session 查询的结果是两个不同的实例
            Assert.assertNotEquals(user1, user2);
            System.out.println(user2==user1);
            //执行删除操作
            userMapper.deleteById(2L);
            //获取 user3
            SysUser user3 = userMapper.selectById(1l);
            System.out.println(user3);
            //这里的 user2 和 user3 是两个不同的实例
            Assert.assertNotEquals(user2, user3);
            System.out.println(user2==user3);
        } finally {
            //关闭 sqlSession
            sqlSession.close();
        }
    }

接下来逐个进行解释:

首先这段代码我们查询了两次数据库,但是只有一次对数据库的查询日志打印,而且最后我们发现user1和user2甚至都是一个对象,这就是因为Mybatis的一级缓存,它存在于SqlSession的生命周期中,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存对应的键值,将键值和查询结果放入到一个Map对象中。所以user2和user1是同一个key的value值。我们如果不想要这种情况,可以做一些修改即可

<!--flushCache="true"在查询前会清空一级缓存-->
	<select id="selectById" resultMap="userMap" flushCache="true">
		select * from sys_user where id= #{id}
	</select>

配置了这个属性,每次都会是先清空一级缓存,这样每次拿到的对象都是不同的实例。

 

接下来是第二个user2和user1却不相同,这是因为第一个SqlSession已经关闭了,而一级缓存和SqlSession是绑定的,所以user2和user1当然不相同

最后一个中我们可以看到user2和user3也不相同,这是为啥呢?我们注意到,我们在查询之前先执行了一个delete的代码,这就是原因。任何的update、insert、delete操作都会清空一级缓存,所以user3和user2也不相等。

2.二级缓存

先上代码

@Test
    public void testL2Cache(){
        //获取 sqlSession
        SqlSession sqlSession = getSqlSession();
        SysUser user1 = null;
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectById 方法,查询 id = 1 的用户
            user1 = userMapper.selectById(1l);
            System.out.println(user1);
            //对当前获取的对象重新赋值
            user1.setUserName("New Name");
            //再次查询获取 id 相同的用户
            SysUser user2 = userMapper.selectById(1l);
            System.out.println(user2);
            System.out.println(user1==user2);
            //虽然我们没有更新数据库,但是这个用户名和我们 user1 重新赋值的名字相同了
            Assert.assertEquals("New Name", user2.getUserName());
            //不仅如何,user2 和 user1 完全就是同一个实例
            System.out.println(user1);
            //Assert.assertNotEquals(user1, user2);
        } finally {
            //关闭当前的 sqlSession
            sqlSession.close();
        }
        System.out.println("开启新的 sqlSession");
        //开始另一个新的 session
        sqlSession = getSqlSession();
        try {
            //获取 UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectById 方法,查询 id = 1 的用户
            SysUser user2 = userMapper.selectById(1l);
            //第二个 session 获取的用户名仍然是 admin
            Assert.assertEquals("New Name", user2.getUserName());
            //这里的 user2 和 前一个 session 查询的结果是两个不同的实例
            Assert.assertNotEquals(user1, user2);
            //获取 user3
            SysUser user3 = userMapper.selectById(1l);
            //这里的 user2 和 user3 是两个不同的实例
            Assert.assertNotEquals(user2, user3);
        } finally {
            //关闭 sqlSession
            sqlSession.close();
        }
    }

注意对应的实体对象需要实现Serializable

public class SysUser implements Serializable {
    private static final long serialVersionUID = -328602757171077630L;
    //...
   
}

同时对应的mapper.xml也需要加入</cache>的配置

运行截图

日志中存在好几条以 Cache Hit Ratio 开头的语句 ,这行日志后面输出的值为当前执行方法的缓存命中率. 在测试第一部分中,第一次查询获取 user1的时候由于没有缓存,所以执行了数据库查询,在第二个查询获取 user2 的时候, user2 user1 是完全相同的实例,这 里使用是一级缓存,所以返回同1个实例.

调用 close 方法关闭 SqlSession 时, SqlSession 才会保存查询数据到二级缓存中 在这之后二级缓存才有了缓存数据 所以可以看到在第 一部分的两次查询时,命中率都是 0

在第二部分测试代码中,再次获取 user2 时,日志中并没有输出数据库查询,而是输出了 命中率,这时的命中率是 0.3333333333333333 这是第三 次查询,并且得到了缓存的值,因此 该方法 共被请求了3 次,有1次命中,所以命中率就是3分之一。 后面再获取 user3 的时候, 就是4次请求, 2次命中,命中率为 0.5 。并且因为可读写缓存的缘故, user2 user3 都是 反序列化得到的结果 ,所以它们不是相同的实例 在这 一部分,这两个实例是读写安全的,其 属性不会互相影响。

图片来源于https://www.cnblogs.com/happyflyingpig/p/7739749.html

二级缓存失效的条件:

1.第一次SqlSession 未提交:SqlSession 在未提交的时候,SQL 语句产生的查询结果还没有放入二级缓存中,这个时候 SqlSession2 在查询的时候是感受不到二级缓存的存在的。

2.更新对二级缓存影响:与一级缓存一样,更新操作很可能对二级缓存造成影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值