MyBatis一级缓存默认开启但仅限同一个SqlSession,执行增删改、close、clearCache或事务提交/回滚均会清空;二级缓存需显式开启且POJO必须实现Serializable。

MyBatis一级缓存默认开启但仅限同一个SqlSession
一级缓存是 MyBatis 默认启用的,不需要任何配置,但它只在同一个 SqlSession 生命周期内有效。一旦调用 sqlSession.close()、sqlSession.clearCache(),或者执行了任何 INSERT/UPDATE/DELETE 语句(哪怕没提交),缓存都会被清空。
常见误判场景:在 Spring 中使用 @Transactional,看似“同一个 Session”,实际每次方法调用可能由 Spring 管理了新的 SqlSession(取决于事务传播行为和是否复用);或手动 new SqlSession 后未复用,导致一级缓存完全失效。
- 一级缓存存储在
BaseExecutor.localCache(一个PerpetualCache实例)中 - 缓存 key 由
MappedStatement.id + offset + limit + SQL + boundSql.parameterObject共同计算,参数对象需正确实现equals/hashCode - 若参数是自定义对象且未重写
equals,两次相同逻辑查询可能命中不了缓存
二级缓存需显式开启且要求 POJO 实现 Serializable
二级缓存作用域是 Mapper namespace 级别,跨 SqlSession 共享,但必须主动开启:在对应 Mapper.xml 中添加 ,或在接口类上加 @CacheNamespace 注解。
它底层默认使用 PerpetualCache(内存 Map),但可替换为 Ehcache、Redis 等第三方实现。不过无论哪种,缓存值序列化/反序列化都绕不开 —— 所以所有被缓存的返回对象(如 User)必须实现 Serializable 接口,否则运行时报 NotSerializableException。
立即学习“Java免费学习笔记(深入)”;
citySHOP是一款集CMS、网店、商品、分类信息、论坛等为一体的城市多用户商城系统,已完美整合目前流行的Discuz! 6.0论坛,采用最新的5.0版PHP+MYSQL技术。面向对象的数据库连接机制,缓存及80%静态化处理,使它能最大程度减轻服务器负担,为您节约建设成本。多级店铺区分及联盟商户地图标注,实体店与虚拟完美结合。个性化的店铺系统,会员后台一体化管理。后台登陆初始网站密匙:LOVES
- 开启二级缓存后,每个
SELECT语句默认可读缓存,每个INSERT/UPDATE/DELETE语句默认清空当前 namespace 下全部缓存(可通过flushCache="false"关闭) -
是安全组合;而useCache="false"会跳过二级缓存(仍走一级) - 多个 namespace 共享同一张表时,彼此无法感知对方的更新,容易脏读 —— 这不是 bug,是设计使然
一级缓存失效的典型代码陷阱
下面这段代码看着像能命中一级缓存,实际第二次查询一定会发 SQL:
SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User u1 = mapper.selectById(1); // 查询一次 mapper.updateName(1, "new"); // 执行了 UPDATE,触发 localCache.clear() User u2 = mapper.selectById(1); // 缓存已清,再次查库 sqlSession.close();
即使 updateName 对应的 SQL 没有真正修改数据(比如 SET name='old' WHERE id=1),只要该语句被 MyBatis 认定为修改操作(SqlCommandType.UPDATE),就会清空当前 SqlSession 的一级缓存。
- 调用
sqlSession.commit()或rollback()也会清空一级缓存 - 不同线程共用一个 SqlSession(如单例模式)会导致并发问题,缓存错乱甚至
ConcurrentModificationException - Spring 管理的
SqlSessionTemplate内部每次操作都可能获取新 SqlSession,所以一级缓存基本不可靠
二级缓存与 MyBatis-Spring 集成时的隐性限制
在 Spring Boot + MyBatis 场景下,即使写了 ,二级缓存也可能不生效 —— 常见原因是 SqlSessionFactoryBean 的 configuration 层级未开启全局开关:cacheEnabled=true(默认 true,但若手动 new Configuration 并设为 false 就会关掉)。
更隐蔽的是:Spring 的声明式事务中,如果 service 方法上标注了 @Transactional(propagation = Propagation.REQUIRES_NEW),那么嵌套事务会新建 SqlSession,导致二级缓存虽然存在,但写入和读取发生在不同缓存实例中(因为 MyBatis-Spring 默认为每个 SqlSession 创建独立的 Cache 实例)。
- 二级缓存不是“全局共享内存”,而是 namespace 维度的 Cache 实例,由
CacheKey定位,不同 SqlSession 通过Cache接口协作 - 若使用自定义
Cache实现(如 RedisCache),务必确保clear()、get()、put()等方法线程安全且支持跨 JVM 实例 - MyBatis 3.4+ 开始,二级缓存默认不缓存
null值,避免穿透;如需缓存空结果,得自己扩展Cache或用@Options(useCache=true)配合拦截器









