深入剖析 MyBatis 缓存机制:从一级缓存到 EhCache 集成优化
在大型应用中,频繁的数据库读写会成为性能瓶颈。MyBatis 提供了完善的缓存机制,通过减少 I/O 操作与数据库查询次数,显著提升系统性能。本文将结合示例,详细介绍 MyBatis 的一级缓存、二级缓存,以及如何集成第三方缓存(以 EhCache 为例)。
1. MyBatis 缓存概述
缓存(Cache):将数据临时存储在内存中,以减少对数据库的直接访问,从而提高程序执行效率。
MyBatis 缓存类型
一级缓存(本地缓存):默认开启,作用域为同一个
SqlSession
。二级缓存(全局缓存):作用域为同一个
SqlSessionFactory
,需要手动配置。第三方缓存集成:例如 EhCache、Memcached 等。
适用范围:仅针对查询(SELECT)语句,Insert/Update/Delete 操作不会被缓存。
2. 一级缓存
特点
默认开启,无需额外配置。
缓存在同一个
SqlSession
生命周期内生效——同一条 SQL、同一参数、同一会话下重复查询时命中缓存。
失效场景
不同
SqlSession
查询条件变化
手动清理
sqlSession.clearCache(); // 清空一级缓存
出现写操作(INSERT/UPDATE/DELETE)
只要同一
SqlSession
中执行了任意写操作,即使针对不同表,也会清空一级缓存。
3. 二级缓存
作用域
针对同一个
SqlSessionFactory
,多个SqlSession
之间可共享。
配置步骤
全局开关(
mybatis-config.xml
中,默认为true
)<setting name="cacheEnabled" value="true"/>
Mapper 文件启用二级缓存
<!-- 在 SqlMapper.xml 根节点内 -->
<cache/>实体类实现序列化
public class User implements Serializable { ... }
提交或关闭
SqlSession
后写入缓存写操作(增删改)同样会导致二级缓存失效。
可选属性(以 <cache>
标签属性形式配置)
属性 | 含义 | 默认值 |
---|---|---|
eviction | 缓存淘汰算法:LRU / FIFO / SOFT / WEAK | LRU |
flushInterval | 缓存刷新间隔(毫秒),不设置则不自动刷新 | — |
readOnly | 是否只读:true 返回同一对象(性能高),false 返回克隆对象 | false |
size | 最大缓存对象数量 | 1024 |
4. 集成 EhCache
为获得更丰富的缓存特性与更高的扩展性,常用 EhCache 替代内置二级缓存。
添加依赖(Maven)
<!-- MyBatis 与 EhCache 集成组件 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<!-- 日志依赖(推荐 Logback) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>创建
ehcache.xml
(放置于类路径根目录)
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir"/>
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" />
</ehcache>修改 Mapper 文件,指定使用 EhCache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
测试示例
public void testEhCache() {
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 第一次查询,写入二级缓存
try (SqlSession s1 = factory.openSession()) {
Car car1 = s1.getMapper(CarMapper.class).selectById(83L);
System.out.println(car1);
}
// 第二次查询,命中缓存
try (SqlSession s2 = factory.openSession()) {
Car car2 = s2.getMapper(CarMapper.class).selectById(83L);
System.out.println(car2);
}
}
- 微信
- 赶快加我聊天吧
- 赶快加我聊天吧