MyBatis SQL执行模块详解

本文深入剖析MyBatis的SQL执行模块,带你全面理解Executor执行器体系、缓存机制、事务管理和批处理原理。

一、MyBatis整体架构与SQL执行模块

在深入SQL执行模块之前,我们先了解MyBatis的整体架构,以及SQL执行模块在其中的核心地位。

1.1 SQL执行模块的核心职责

SQL执行模块主要承担以下核心职责:

复制代码
1、SQL执行:根据MappedStatement执行SQL语句,并返回结果
2、缓存管理:管理一级缓存和二级缓存,提高查询性能
3、事务管理:控制数据库事务的提交、回滚和关闭
4、批处理支持:支持批量操作,提升数据修改效率
5、Statement管理:管理JDBC Statement对象的生命周期
6、插件拦截:提供拦截点,支持插件扩展

1.2 Executor接口体系

Executor是SQL执行模块的顶层接口,定义了SQL执行的基本方法:

复制代码
public interface Executor {
    // 执行查询(带缓存Key)
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, CacheKey cacheKey, BoundSql boundSql);

    // 执行查询
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler);

    // 执行更新(插入、更新、删除)
    int update(MappedStatement ms, Object parameter);

    // 刷新批量操作
    List<BatchResult> flushStatements();

    // 提交事务
    void commit(boolean required);

    // 回滚事务
    void rollback(boolean required);

    // 创建CacheKey
    CacheKey createCacheKey(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql);

    // 判断是否缓存
    boolean isCached(MappedStatement ms, CacheKey cacheKey);

    // 清空本地缓存
    void clearLocalCache();

    // 获取事务
    Transaction getTransaction();

    // 关闭执行器
    void close(boolean forceRollback);
}

二、Executor执行器架构

MyBatis提供了多种Executor实现,以适应不同的使用场景。

2.1 Executor继承体系

Executor采用了装饰器模式,提供了灵活的功能扩展:

复制代码
Executor (接口)

├── BaseExecutor (抽象基类)

│   ├── SimpleExecutor (简单执行器)

│   ├── ReuseExecutor (可重用执行器)

│   └── BatchExecutor (批处理执行器)

└── CachingExecutor (缓存执行器)

2.2 BaseExecutor抽象基类

BaseExecutor实现了Executor接口的大部分功能,定义了SQL执行的基本流程:

复制代码
public abstract class BaseExecutor implements Executor {
    protected Transaction transaction;
    protected Executor wrapper;
    protected ConcurrentLinkedQueue<DeferredLoad<?>> deferredLoads;
    protected PerpetualCache localCache;        // 一级缓存
    protected PerpetualCache localOutputParameterCache;
    protected Configuration configuration;

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
        // 1. 创建BoundSql
        BoundSql boundSql = ms.getBoundSql(parameter);

        // 2. 创建CacheKey
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

        // 3. 执行查询
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
                             ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
        // 检查本地缓存
        List<E> list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;

        if (list != null) {
            return list;
        }

        // 执行数据库查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        return list;
    }
}

2.3 SimpleExecutor简单执行器

SimpleExecutor是最基础的执行器实现,每次执行SQL都会创建新的Statement对象:

复制代码
public class SimpleExecutor extends BaseExecutor {
    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                              ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            // 1. 创建Configuration对象
            Configuration configuration = ms.getConfiguration();

            // 2. 创建StatementHandler
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter,
                                                                        rowBounds, resultHandler, boundSql);

            // 3. 创建Statement
            stmt = prepareStatement(handler, ms.getStatementLog());

            // 4. 执行查询
            return handler.<E>query(stmt, resultHandler);
        } finally {
            // 5. 关闭Statement
            closeStatement(stmt);
        }
    }

    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter,
                                                                        RowBounds.DEFAULT, null, null);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.update(stmt);
        } finally {
            closeStatement(stmt);
        }
    }
}

2.4 ReuseExecutor可重用执行器

ReuseExecutor会缓存Statement对象,相同SQL可以重用Statement,减少Statement创建开销:

复制代码
public class ReuseExecutor extends BaseExecutor {
    private final Map<String, Statement> statementMap = new HashMap<>();

    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                              ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter,
                                                                    rowBounds, resultHandler, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog(), boundSql.getSql());
        return handler.<E>query(stmt, resultHandler);
    }

    private Statement prepareStatement(StatementHandler handler, Log statementLog, String sql) throws SQLException {
        Statement stmt;
        // 尝试从缓存中获取Statement
        stmt = statementMap.get(sql);
        if (stmt == null) {
            // 缓存未命中,创建新的Statement
            stmt = prepareStatement(handler, statementLog);
            statementMap.put(sql, stmt);
        }
        return stmt;
    }
}

2.5 BatchExecutor批处理执行器

BatchExecutor专门用于批量操作,会将多个SQL语句批量执行:

复制代码
public class BatchExecutor extends BaseExecutor {
    private final List<Statement> statementList = new ArrayList<>();
    private final List<BatchResult> batchResultList = new ArrayList<>();
    private String currentSql;
    private MappedStatement currentStatement;

    @Override
    public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject,
                                                                    RowBounds.DEFAULT, null, null);

        BoundSql boundSql = ms.getBoundSql(parameterObject);
        String sql = boundSql.getSql();
        Statement stmt;

        // 检查是否需要切换SQL
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
            // 相同SQL,复用Statement
            int last = statementList.size() - 1;
            stmt = statementList.get(last);
        } else {
            // 不同SQL,创建新Statement
            currentSql = sql;
            currentStatement = ms;
            stmt = prepareStatement(handler);
            statementList.add(stmt);
            batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }

        // 添加批处理
        handler.parameterize(stmt);
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
    }

    @Override
    public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
        List<BatchResult> results = new ArrayList<>();
        try {
            for (int i = 0, n = statementList.size(); i < n; i++) {
                Statement stmt = statementList.get(i);
                BatchResult batchResult = batchResultList.get(i);

                try {
                    if (!isRollback) {
                        // 执行批处理
                        int[] updateCounts = stmt.executeBatch();
                        batchResult.setUpdateCounts(updateCounts);
                    }
                    results.add(batchResult);
                } catch (SQLException e) {
                    throw new BatchExecutorException("Error updating database. Cause: " + e, e, batchResult);
                }
            }
            return results;
        } finally {
            // 清空缓存
            statementList.clear();
            batchResultList.clear();
            currentSql = null;
            currentStatement = null;
        }
    }
}

2.6 CachingExecutor缓存执行器

CachingExecutor是Executor的装饰器,在底层Executor之上增加了二级缓存功能:

复制代码
public class CachingExecutor implements Executor {
    private final Executor delegate;
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
                            ResultHandler resultHandler) throws SQLException {
        // 1. 获取BoundSql
        BoundSql boundSql = ms.getBoundSql(parameter);

        // 2. 创建CacheKey
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

        // 3. 查询缓存
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
                            ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 1. 检查二级缓存
        Cache cache = ms.getCache();
        if (cache != null) {
            // 刷新缓存(如果需要)
            flushCacheIfRequired(ms);

            // 检查缓存是否命中
            if (ms.isUseCache() && resultHandler == null) {
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list != null) {
                    return list;
                }
            }
        }

        // 2. 缓存未命中,委托给底层Executor执行
        List<E> list = delegate.<E>query(ms, parameter, rowBounds, resultHandler, key, boundSql);

        // 3. 将结果放入二级缓存
        if (cache != null) {
            tcm.putObject(cache, key, list);
        }

        return list;
    }
}

三、SQL执行流程

SQL的执行流程是Executor的核心工作流程。

3.1 完整执行流程

以查询操作为例,完整的SQL执行流程如下:

复制代码
// 1. SqlSession调用Executor
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 1.1 获取MappedStatement
        MappedStatement ms = configuration.getMappedStatement(statement);

        // 1.2 调用Executor执行查询
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
    }
}

// 2. Executor执行查询
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    // 2.1 获取BoundSql
    BoundSql boundSql = ms.getBoundSql(parameter);

    // 2.2 创建CacheKey
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

    // 2.3 执行查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

// 3. 检查一级缓存
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
                        ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
    List<E> list;

    // 3.1 检查一级缓存
    if (resultHandler == null) {
        list = (List<E>) localCache.getObject(key);
    }

    if (list != null) {
        return list;
    }

    // 3.2 缓存未命中,查询数据库
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    return list;
}

// 4. 查询数据库
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                     ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
    List<E> list;

    // 4.1 占位缓存,处理循环依赖
    localCache.putObject(key, EXECUTION_PLACEHOLDER);

    try {
        // 4.2 执行查询
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        // 4.3 移除占位符
        localCache.removeObject(key);
    }

    // 4.4 将结果放入一级缓存
    localCache.putObject(key, list);

    // 4.5 处理延迟加载
    if (ms.getConfiguration().isLazyLoadingEnabled()) {
        if (deferredLoads != null && !deferredLoads.isEmpty()) {
            deferredLoads.clear();
        }
    }

    return list;
}

// 5. 执行实际查询
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                       ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

3.2 StatementHandler的作用

StatementHandler负责Statement的创建、参数设置和SQL执行:

复制代码
public interface StatementHandler {
    // 准备Statement
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

    // 参数化Statement
    void parameterize(Statement statement) throws SQLException;

    // 执行查询
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

    // 执行更新
    int update(Statement statement) throws SQLException;

    // 批处理
    void batch(Statement statement) throws SQLException;

    // 获取BoundSql
    BoundSql getBoundSql();
}

3.3 ResultSetHandler的作用

ResultSetHandler负责将ResultSet映射为Java对象:

复制代码
public interface ResultSetHandler {
    // 处理结果集
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;

    // 处理游标结果集
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    // 处理输出参数
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}

四、缓存管理机制

MyBatis提供了两级缓存机制,有效提升查询性能。

4.1 一级缓存(Local Cache)

一级缓存是SqlSession级别的缓存,默认开启,作用域是当前SqlSession:

复制代码
public class PerpetualCache implements Cache {
    private final String id;
    private final Map<Object, Object> cache = new HashMap<>();

    @Override
    public void putObject(Object key, Object value) {
        cache.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return cache.get(key);
    }

    @Override
    public Object removeObject(Object key) {
        return cache.remove(key);
    }

    @Override
    public void clear() {
        cache.clear();
    }
}

一级缓存的特点:

复制代码
1、作用域:SqlSession级别
2、生命周期:与SqlSession相同,SqlSession关闭时缓存清空
3、缓存Key:由MappedStatement ID、参数SQL、分页参数等组成
4、自动失效:执行增删改操作时,一级缓存会自动清空

4.2 二级缓存(Global Cache)

二级缓存是Mapper级别的缓存,需要手动配置,作用域是Namespace:

复制代码
<!-- 在Mapper XML中配置二级缓存 -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>

二级缓存的特点:

复制代码
1、作用域:Namespace(Mapper)级别
2、生命周期:应用级别,直到应用关闭
3、跨Session共享:多个SqlSession可以共享
4、配置灵活:可以自定义缓存策略

4.3 缓存Key的构建

CacheKey由多个元素组成,确保缓存键的唯一性:

复制代码
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    CacheKey cacheKey = new CacheKey();

    // 1. MappedStatement ID
    cacheKey.update(ms.getId());

    // 2. 分页参数
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());

    // 3. SQL语句
    cacheKey.update(boundSql.getSql());

    // 4. 参数值
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();

    for (ParameterMapping parameterMapping : parameterMappings) {
        String propertyName = parameterMapping.getProperty();
        Object value;

        if (boundSql.hasAdditionalParameter(propertyName)) {
            value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
            value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
        } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
        }

        cacheKey.update(value);
    }

    // 5. Environment ID
    if (configuration.getEnvironment() != null) {
        cacheKey.update(configuration.getEnvironment().getId());
    }

    return cacheKey;
}

4.4 缓存装饰器模式

MyBatis使用装饰器模式实现缓存功能的增强:

复制代码
// 基础缓存
Cache cache = new PerpetualCache("myCache");

// 添加LRU淘汰策略
cache = new LruCache(cache);

// 添加定时刷新
cache = new ScheduledCache(cache);

// 添加序列化支持
cache = new SerializedCache(cache);

// 添加日志记录
cache = new LoggingCache(cache);

// 添加同步支持
cache = new SynchronizedCache(cache);

4.5 缓存使用示例

复制代码
// 一级缓存示例
SqlSession session = sqlSessionFactory.openSession();
try {
    UserMapper mapper = session.getMapper(UserMapper.class);

    // 第一次查询,访问数据库
    User user1 = mapper.selectById(1L);

    // 第二次查询,从一级缓存获取
    User user2 = mapper.selectById(1L);

    // user1 == user2,同一对象
} finally {
    session.close();
}

// 二级缓存示例
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
try {
    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    UserMapper mapper2 = session2.getMapper(UserMapper.class);

    // session1第一次查询,访问数据库
    User user1 = mapper1.selectById(1L);

    // session1提交,将数据写入二级缓存
    session1.commit();

    // session2查询,从二级缓存获取
    User user2 = mapper2.selectById(1L);

    // user1 equals user2(不同对象,但值相等)
} finally {
    session1.close();
    session2.close();
}

五、事务管理

事务管理是数据库操作的重要组成部分,Executor负责事务的创建、提交和回滚。

5.1 Transaction接口

Transaction是事务管理的顶层接口:

复制代码
public interface Transaction {
    // 获取数据库连接
    Connection getConnection() throws SQLException;

    // 提交事务
    void commit() throws SQLException;

    // 回滚事务
    void rollback() throws SQLException;

    // 关闭连接
    void close() throws SQLException;

    // 获取事务超时时间
    Integer getTimeout() throws SQLException;
}

5.2 事务隔离级别

MyBatis支持标准的事务隔离级别:

复制代码
public enum IsolationLevel {
    NONE(Connection.TRANSACTION_NONE),
    READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
    READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
    REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
    SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
}

配置示例:

复制代码
<settings>
    <setting name="defaultTransactionIsolationLevel" value="READ_COMMITTED"/>
</settings>

5.3 事务管理流程

Executor的事务管理流程:

复制代码
// 提交事务
@Override
public void commit(boolean required) throws SQLException {
    if (closed) {
        throw new ExecutorException("Cannot commit, transaction is already closed");
    }

    // 1. 清空本地缓存
    clearLocalCache();

    // 2. 刷新批量操作
    List<BatchResult> batchResults = flushStatements(true);

    // 3. 提交事务
    if (required) {
        transaction.commit();
    }

    return batchResults;
}

// 回滚事务
@Override
public void rollback(boolean required) throws SQLException {
    if (closed) {
        throw new ExecutorException("Cannot rollback, transaction is already closed");
    }

    try {
        // 1. 清空本地缓存
        clearLocalCache();

        // 2. 刷新批量操作
        flushStatements(true);

        // 3. 回滚事务
        if (required) {
            transaction.rollback();
        }
    } finally {
        if (required) {
            // 4. 关闭事务
            transaction.close();
        }
    }
}

5.4 自动提交与手动提交

复制代码
// 自动提交模式
SqlSession session = sqlSessionFactory.openSession(true);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insert(user);
    // 无需手动提交,自动提交
} finally {
    session.close();
}

// 手动提交模式(默认)
SqlSession session = sqlSessionFactory.openSession();
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insert(user);
    // 需要手动提交
    session.commit();
} catch (Exception e) {
    // 异常时回滚
    session.rollback();
    throw e;
} finally {
    session.close();
}

5.5 Spring事务集成

在Spring环境中,通常使用Spring的事务管理:

复制代码
@Service
@Transactional
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void updateUser(User user) {
        // Spring管理事务,无需手动提交
        userMapper.update(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 转账操作:同一事务
        userMapper.decrease(fromId, amount);
        userMapper.increase(toId, amount);
    }
}

六、批处理机制

批处理可以显著提升批量操作的性能。

6.1 批处理配置

使用批处理需要指定ExecutorType:

复制代码
// 创建批处理SqlSession
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);

    // 批量插入
    for (User user : userList) {
        mapper.insert(user);
    }

    // 刷新并执行批处理
    session.flushStatements();

    // 提交事务
    session.commit();
} finally {
    session.close();
}

6.2 批处理原理

BatchExecutor的工作原理:

复制代码
1、SQL缓存:相同SQL复用Statement
2、参数累积:多次调用addBatch()
3、批量执行:调用executeBatch()
4、结果返回:返回每条SQL的执行结果
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject,
                                                                RowBounds.DEFAULT, null, null);

    BoundSql boundSql = ms.getBoundSql(parameterObject);
    String sql = boundSql.getSql();
    Statement stmt;

    // 检查是否可以复用Statement
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
        stmt = statementList.get(statementList.size() - 1);
    } else {
        stmt = prepareStatement(handler);
        statementList.add(stmt);
        batchResultList.add(new BatchResult(ms, sql, parameterObject));
        currentSql = sql;
        currentStatement = ms;
    }

    // 参数化并添加到批处理
    handler.parameterize(stmt);
    handler.batch(stmt);

    return BATCH_UPDATE_RETURN_VALUE;
}

6.3 批处理性能优化

批处理的性能优化建议:

复制代码
1、合理控制批次大小:避免一次性提交过多SQL
2、使用BatchExecutor:批量操作时使用批处理执行器
3、关闭自动提交:手动控制事务提交
4、合理使用flushStatements:控制批处理执行时机
// 分批处理示例
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);

    int batchSize = 1000;
    List<List<User>> batches = Lists.partition(userList, batchSize);

    for (List<User> batch : batches) {
        for (User user : batch) {
            mapper.insert(user);
        }
        // 每批次刷新一次
        session.flushStatements();
        session.clearCache();
    }

    session.commit();
} finally {
    session.close();
}

6.4 批处理返回结果

批处理返回的是每条SQL影响的行数:

复制代码
List<BatchResult> results = session.flushStatements();

for (BatchResult result : results) {
    int[] updateCounts = result.getUpdateCounts();
    for (int count : updateCounts) {
        System.out.println("影响行数: " + count);
    }
}

6.5 批处理注意事项

复制代码
1.Statement限制:数据库对PreparedStatement数量有限制
2.内存占用:大量SQL会占用较多内存
3.错误处理:批处理中某条SQL失败,需要特别处理
4.日志输出:批处理日志可能较多,建议适当调整日志级别

七、最佳实践

7.1 Executor选择建议

场景 推荐Executor 说明
一般查询 SIMPLE 默认选择,每次创建新Statement
重复查询多 REUSE 复用Statement,减少创建开销
批量操作 BATCH 显著提升批量操作性能
启用二级缓存 CACHING 在其他Executor基础上增加缓存

7.2 性能优化建议

复制代码
1、合理使用缓存:根据业务特点选择缓存级别
2、批量操作优化:大量数据修改使用BatchExecutor
3、及时清理缓存:避免缓存数据过期
4、控制事务范围:事务尽量小,减少锁竞争
5、使用连接池:避免频繁创建连接

7.3 常见问题解决

问题1:一级缓存未生效

复制代码
// 问题代码
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1L);
User user2 = mapper.selectById(1L);
// user1 != user2,缓存未生效

// 原因:两次查询不在同一SqlSession
// 解决:确保在同一个SqlSession中查询

问题2:二级缓存脏数据

复制代码
<!-- 解决方案:设置刷新间隔 -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="false"/>

问题3:批处理内存溢出

复制代码
// 解决方案:分批处理
int batchSize = 1000;
for (int i = 0; i < totalSize; i += batchSize) {
    List<User> batch = userList.subList(i, Math.min(i + batchSize, totalSize));
    processBatch(session, batch);
    session.flushStatements();
    session.clearCache();
}

八、总结

MyBatis的SQL执行模块是整个框架的核心执行引擎,通过精心设计的Executor体系,实现了高效的SQL执行、灵活的缓存管理、可靠的事务控制和强大的批处理能力。

相关推荐
_Aaron___2 小时前
MyBatis 连接缓慢问题排查与解决实战
mybatis
飞Link3 小时前
【MySQL】Linux(CentOS7)下安装MySQL8教程
linux·数据库·mysql
数据库生产实战3 小时前
Oracle的_segment_count和3个event对高并发事务与索引性能的影响分析
数据库·oracle
程序员侠客行3 小时前
Mybatis二级缓存实现详解
java·数据库·后端·架构·mybatis
Tipriest_4 小时前
linux中的文本分接流tee命令介绍
linux·服务器·数据库
爱喝水的鱼丶4 小时前
SAP-ABAP:在SAP世界里与特殊字符“斗智斗勇”:一份来自实战的避坑指南
运维·服务器·数据库·学习·sap·abap·特殊字符
阿拉伯柠檬4 小时前
MySQL内置函数
linux·数据库·mysql·面试
小Mie不吃饭4 小时前
2025 Oracle小白零基础到入门的学习路线
数据库·oracle
麒qiqi4 小时前
SQLite3 数据库
数据库·oracle