MyBatis 缓存与插件
一级缓存(SqlSession 级别)
默认开启,同一个 SqlSession 内相同查询只执行一次 SQL:
try (SqlSession session = factory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User u1 = mapper.selectById(1L); // 查数据库
User u2 = mapper.selectById(1L); // 命中一级缓存
System.out.println(u1 == u2); // true
}
一级缓存失效场景:
- 不同 SqlSession
- SqlSession 内执行了 INSERT/UPDATE/DELETE
- 手动 clearCache()
二级缓存(Mapper 级别)
跨 SqlSession 共享,需显式开启:
<!-- mybatis-config.xml -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="false"/>
</mapper>
可选属性:
| 属性 | 说明 | 默认 |
|---|---|---|
| eviction | 回收策略 | LRU |
| flushInterval | 刷新间隔(ms) | 不刷新 |
| size | 最大对象数 | 1024 |
| readOnly | 只读=true 性能高 | false |
自定义缓存
实现 Cache 接口接 Redis/Ehcache:
public class RedisCache implements Cache {
private final String id;
public RedisCache(String id) { this.id = id; }
@Override public String getId() { return id; }
@Override public void putObject(Object key, Object value) {
redis.set(key.toString(), serialize(value));
}
@Override public Object getObject(Object key) {
return deserialize(redis.get(key.toString()));
}
@Override public Object removeObject(Object key) {
return redis.del(key.toString());
}
@Override public void clear() { /* 清空 namespace 缓存 */ }
@Override public int getSize() { return redis.dbSize().intValue(); }
}
配置:
<cache type="com.example.cache.RedisCache"/>
插件机制(Interceptor)
MyBatis 允许拦截以下方法:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
分页插件原理
PageHelper 就是通过拦截 Executor.query() 实现的:
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1. 获取参数中的 Page 对象
// 2. 执行 COUNT 查询获取总数
// 3. 修改原 SQL 添加 LIMIT 子句
// 4. 执行分页查询
return invocation.proceed();
}
}
使用 PageHelper
// Maven
// pagehelper-spring-boot-starter 1.4.7
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);
// pageInfo.getTotal(); // 总记录数
// pageInfo.getPages(); // 总页数