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)级别。

配置步骤

  1. 全局配置开启二级缓存

    <settings>
     <setting name="cacheEnabled" value="true"/>
    </settings>
  2. 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>

性能对比

图1

实践建议

  • 大批量操作使用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的高级特性为复杂业务场景提供了灵活解决方案。合理使用缓存可以显著提升性能,插件机制提供了强大的扩展能力,而批处理则优化了大批量数据操作的效率。在实际项目中,应根据具体需求选择合适的技术组合,并注意各种特性的适用场景和限制条件。

添加新评论