Mybatis的缓存机制

Mybatis的缓存机制

Mybatis框架默认是有2套缓存机制的,分别称之一级缓存和二级缓存。

Mybatis框架的一级缓存也称之为“会话(Session)缓存”,默认是开启的,且无法关闭!

一级缓存必须保证多次的查询操作满足:同一个SqlSession、同一个Mapper、执行相同的SQL查询、使用相同的参数。

关于一级缓存的典型表现可以通过测试以下代码进行观察:

@Slf4j
@SpringBootTest
public class MybatisCacheTests {

    @Autowired
    SqlSessionFactory sqlSessionFactory;

    @Test
    void l1Cache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        Long id = 1L;
        log.debug("开始第【1】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result1 = brandMapper.getStandardById(id);
        log.debug("第【1】查询结果的hashCode()值为:{}", result1.hashCode());
        log.debug("开始第【2】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result2 = brandMapper.getStandardById(id);
        log.debug("第【2】查询结果的hashCode()值为:{}", result2.hashCode());

        log.debug("第【1】次的查询结果与第【2】的查询结果进行对比,结果:{}", result1 == result2);

        id = 2L;
        log.debug("开始第【1】次执行根据ID【2】查询品牌详情……");
        BrandStandardVO result3 = brandMapper.getStandardById(id);
        log.debug("第【1】查询结果的hashCode()值为:{}", result3.hashCode());
        log.debug("开始第【2】次执行根据ID【2】查询品牌详情……");
        BrandStandardVO result4 = brandMapper.getStandardById(id);
        log.debug("第【2】查询结果的hashCode()值为:{}", result4.hashCode());

        id = 1L;
        log.debug("开始第【3】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result5 = brandMapper.getStandardById(id);
        log.debug("第【3】查询结果的hashCode()值为:{}", result5.hashCode());
    }

}

一级缓存会因为以下任意一种原因而消失:

  • 调用SqlSession对象的clearCache()方法,将清除当前会话中此前产生的所有一级缓存数据
  • 当前执行了任何写操作(增 / 删 / 改),无论任何数据有没有发生变化,都会清空此前产生的缓存数据

演示代码:

@Slf4j
@SpringBootTest
public class MybatisCacheTests {

    @Autowired
    SqlSessionFactory sqlSessionFactory;

    @Test
    void l1Cache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

        Long id = 1L;
        log.debug("开始第【1】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result1 = brandMapper.getStandardById(id);
        log.debug("第【1】查询结果的hashCode()值为:{}", result1.hashCode());
        log.debug("开始第【2】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result2 = brandMapper.getStandardById(id);
        log.debug("第【2】查询结果的hashCode()值为:{}", result2.hashCode());

        log.debug("第【1】次的查询结果与第【2】的查询结果进行对比,结果:{}", result1 == result2);

        id = 2L;
        log.debug("开始第【1】次执行根据ID【2】查询品牌详情……");
        BrandStandardVO result3 = brandMapper.getStandardById(id);
        log.debug("第【1】查询结果的hashCode()值为:{}", result3.hashCode());
        log.debug("开始第【2】次执行根据ID【2】查询品牌详情……");
        BrandStandardVO result4 = brandMapper.getStandardById(id);
        log.debug("第【2】查询结果的hashCode()值为:{}", result4.hashCode());

        // log.debug("即将调用SqlSession对象的clearCache()方法清除缓存……");
        // sqlSession.clearCache();
        // log.debug("已经清除以前产生的缓存数据!");

        log.debug("即将执行写操作……");
        Brand brand = new Brand();
        brand.setId(1L);
        brand.setName("华为2022");
        brandMapper.update(brand);
        log.debug("执行写操作,完成!");

        id = 1L;
        log.debug("开始第【3】次执行根据ID【1】查询品牌详情……");
        BrandStandardVO result5 = brandMapper.getStandardById(id);
        log.debug("第【3】查询结果的hashCode()值为:{}", result5.hashCode());

        id = 2L;
        log.debug("开始第【3】次执行根据ID【2】查询品牌详情……");
        BrandStandardVO result6 = brandMapper.getStandardById(id);
        log.debug("第【3】查询结果的hashCode()值为:{}", result6.hashCode());
    }

}

Mybatis框架的二级缓存也称之为“namespace缓存”,是作用于某个namespace的,具体 表现为:无论是否为同一个SqlSession,只要执行的是相同的Mapper的查询,且查询参数相同,就可以应用二级缓存。

在使用Spring Boot与Mybatis的项目中,二级缓存默认是全局开启的,但各namespace默认并未开启,如果需要在namespace中开启二级缓存,需要在XML文件中添加<cache/>标签,则表示当前XML中所有查询都开启了二级缓存!

需要注意:使用二级缓存时,需要保证查询结果的类型实现了Serializable接口!

另外,还可以在<select>标签上配置useCache属性,以配置“是否使用缓存”,此属性的默认值为true,表示“使用缓存”。

当应用二级缓存后,在日志上会提示[Cache Hit Ratio],表示“当前namespace缓存命中率”。

与一级缓存相同,只需要发生任何写操作,都会自动清除缓存数据!

Mybatis在查询数据时,会优先尝试从二级缓存中查询是否存在缓存数据,如果命中,将直接返回,如果未命中,则尝试从一级缓存中查询是否存在缓存数据,如果命中,将返回,如果仍未命中,将执行数据库查询。

二级缓存的示例代码:

@Autowired
BrandMapper brandMapper;

@Test
void l2Cache() {
    Long id = 1L;
    log.debug("开始第【1】次执行根据ID【1】查询品牌详情……");
    BrandStandardVO result1 = brandMapper.getStandardById(id);
    log.debug("第【1】查询结果的hashCode()值为:{}", result1.hashCode());

    log.debug("开始第【2】次执行根据ID【1】查询品牌详情……");
    BrandStandardVO result2 = brandMapper.getStandardById(id);
    log.debug("第【2】查询结果的hashCode()值为:{}", result2.hashCode());

    log.debug("即将执行写操作……");
    Brand brand = new Brand();
    brand.setId(6L);
    brand.setName("微软2022");
    brandMapper.update(brand);
    log.debug("执行写操作,完成!");

    log.debug("开始第【3】次执行根据ID【1】查询品牌详情……");
    BrandStandardVO result3 = brandMapper.getStandardById(id);
    log.debug("第【3】查询结果的hashCode()值为:{}", result3.hashCode());

    log.debug("开始第【4】次执行根据ID【1】查询品牌详情……");
    BrandStandardVO result4 = brandMapper.getStandardById(id);
    log.debug("第【4】查询结果的hashCode()值为:{}", result4.hashCode());

    log.debug("开始第【5】次执行根据ID【1】查询品牌详情……");
    BrandStandardVO result5 = brandMapper.getStandardById(id);
    log.debug("第【5】查询结果的hashCode()值为:{}", result5.hashCode());
}
提示:一定要在XML中添加<cache>才能够使用二级缓存。
  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟程序员z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值