为什么要有缓存?

缓存是为了提高数据库查询速度,在查询时先走缓存,缓存是在内存中的,缓存没有命中时,再进行数据库的查询(磁盘IO)。

类似于计算机组成原理的cache,频繁访问的内容放在cache中会大幅度提高运作效率

一级缓存(默认开启)

一级缓存是sqlsession级别的缓存,在单个session中,反复查询数据库的时候会先走缓存,若二级缓存开启,则先走二级缓存,再走一级缓存。若二级缓存关闭,则直接走一级缓存。

二级缓存(默认关闭)

作用域边界:Mapper 的 namespace(命名空间)

每个 namespace 对应一个独立的二级缓存区域,不同 namespace 的缓存互不干扰;

即使两个 Mapper(比如 UserMapper 和 OrderMapper)查询同一张数据库表,只要 namespace 不同,它们的二级缓存也不会共享数据;

全局图

二级缓存导致脏读的情况

在进行联表查询的时候,查询结果会缓存在多个表里(查询出来的所有字段都会存储,就算字段不属于这个表而是属于连接后的大表)

但是若其中一个表更新后,只会清理那一个namespace的缓存区域

导致查询其他表的时候,依旧走二级缓存命中,查出旧的信息,产生脏读

一般用Redis代替二级缓存

清空一级缓存的方式

Select元素的flushCache设置为true方便一些

清空二级缓存的方式

1、二级缓存的生命周期和 SqlSessionFactory 一致,只要 SqlSessionFactory 不被销毁(比如应用不重启),缓存就会存在(除非手动清理或触发自动淘汰);

手动清理:


public class MyBatisCacheCleaner {

    // 注入Spring管理的SqlSessionFactory
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 清理指定namespace的二级缓存
     * @param namespace Mapper的命名空间(如:com.example.mapper.UserMapper)
     */
    public void clearCacheByNamespace(String namespace) {
        // 1. 获取MyBatis全局配置
        Configuration configuration = sqlSessionFactory.getConfiguration();
        // 2. 根据namespace获取对应的缓存对象
        org.apache.ibatis.cache.Cache cache = configuration.getCache(namespace);
        if (cache != null) {
            // 3. 手动清理该namespace的二级缓存
            cache.clear();
            System.out.println("成功清理namespace [" + namespace + "] 的二级缓存");
        } else {
            System.out.println("未找到namespace [" + namespace + "] 的缓存配置");
        }
    }
}

2、当执行增删改操作时,MyBatis 会默认清空当前 namespace 下的所有二级缓存,避免缓存数据与数据库不一致。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐