MyBatis高级特性解析:缓存、插件与批处理
MyBatis高级特性深度解析:缓存、插件与批处理
MyBatis作为一款优秀的持久层框架,除了基础的CRUD操作外,还提供了许多高级特性来满足复杂业务场景的需求。本文将深入探讨MyBatis的缓存机制、插件开发和批处理等高级功能。
一、缓存机制
1. 一级缓存(SqlSession级别)
一级缓存是MyBatis默认开启的缓存机制,作用于SqlSession生命周期内。
// 示例:一级缓存演示
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.selectById(1); // 第一次查询,访问数据库
User user2 = mapper.selectById(1); // 第二次查询,直接从缓存获取
System.out.println(user1 == user2); // 输出true,同一个对象
} finally {
sqlSession.close();
}
特点:
- 默认开启,无需配置
- 基于SqlSession级别,不同SqlSession隔离
- 执行增删改操作或调用
clearCache()
会清空缓存
2. 二级缓存(Mapper级别)
二级缓存是跨SqlSession的缓存,作用于Mapper(namespace)级别。
配置步骤:
全局配置开启二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
Mapper XML中配置缓存
<mapper namespace="com.example.UserMapper"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!-- 其他SQL定义 --> </mapper>
缓存策略:
LRU
- 最近最少使用(默认)FIFO
- 先进先出SOFT
- 软引用WEAK
- 弱引用
3. 自定义缓存实现
MyBatis支持集成第三方缓存如Redis、Ehcache等。
public class RedisCache implements Cache {
private final String id;
private final RedisTemplate<String, Object> redisTemplate;
public RedisCache(String id) {
this.id = id;
this.redisTemplate = RedisConfig.getRedisTemplate();
}
// 实现Cache接口方法...
}
配置使用自定义缓存:
<cache type="com.example.RedisCache"/>
实践建议:
- 查询频繁但更新少的表适合使用二级缓存
- 涉及多表联查的结果谨慎使用缓存
- 分布式环境建议使用Redis等分布式缓存
二、插件开发
MyBatis插件通过拦截器机制实现,可以拦截四大核心对象:
- Executor (执行器)
- StatementHandler (SQL预处理)
- ParameterHandler (参数处理)
- ResultSetHandler (结果集处理)
1. 基本实现
@Intercepts({
@Signature(type = Executor.class, method = "update",
args = {MappedStatement.class, Object.class})
})
public class ExamplePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 前置处理
Object result = invocation.proceed(); // 执行原方法
// 后置处理
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 读取配置参数
}
}
2. 分页插件示例
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class})
})
public class PaginationPlugin implements Interceptor {
// 实现分页逻辑...
}
实践建议:
- 优先使用成熟插件如PageHelper
- 避免过度拦截影响性能
- 注意拦截器执行顺序问题
三、批处理
MyBatis提供了高效的批处理操作支持。
1. 批量插入
// JDBC方式
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i = 0; i < 1000; i++) {
mapper.insert(new User("user" + i));
if (i % 200 == 0) {
sqlSession.flushStatements(); // 分批提交
}
}
sqlSession.commit();
} finally {
sqlSession.close();
}
// foreach方式
<insert id="batchInsert">
INSERT INTO user(name) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name})
</foreach>
</insert>
2. 批量更新
<update id="batchUpdate">
<foreach collection="list" item="item" separator=";">
UPDATE user SET name=#{item.name} WHERE id=#{item.id}
</foreach>
</update>
性能对比:
实践建议:
- 大批量操作使用ExecutorType.BATCH模式
- MySQL的foreach批量插入注意调整max_allowed_packet参数
- Oracle批量操作建议使用BEGIN...END语句块
四、存储过程调用
MyBatis支持存储过程的调用和结果处理。
<select id="callProcedure" statementType="CALLABLE">
{call get_user_by_id(
#{id,mode=IN,jdbcType=INTEGER},
#{name,mode=OUT,jdbcType=VARCHAR}
)}
</select>
五、延迟加载
延迟加载可以优化关联查询性能。
配置开启:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
关联查询示例:
<resultMap id="userWithOrders" type="User">
<id property="id" column="id"/>
<collection property="orders" ofType="Order"
select="selectOrdersByUserId" column="id"
fetchType="lazy"/>
</resultMap>
实践建议:
- 一对多、多对多关联适合延迟加载
- 注意N+1查询问题
- 事务范围内使用延迟加载
总结
MyBatis的高级特性为复杂业务场景提供了灵活解决方案。合理使用缓存可以显著提升性能,插件机制提供了强大的扩展能力,而批处理则优化了大批量数据操作的效率。在实际项目中,应根据具体需求选择合适的技术组合,并注意各种特性的适用场景和限制条件。