最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis SQL執(zhí)行模塊的使用及解讀

 更新時(shí)間:2026年01月01日 17:08:22   作者:雨中飄蕩的記憶  
本文詳細(xì)介紹了MyBatis的SQL執(zhí)行模塊,涵蓋了Executor執(zhí)行器體系、緩存機(jī)制、事務(wù)管理和批處理原理等內(nèi)容,幫助讀者全面理解MyBatis在執(zhí)行SQL時(shí)的各個(gè)關(guān)鍵點(diǎn)

本文深入剖析MyBatis的SQL執(zhí)行模塊,帶你全面理解Executor執(zhí)行器體系、緩存機(jī)制、事務(wù)管理和批處理原理。

一、MyBatis整體架構(gòu)與SQL執(zhí)行模塊

在深入SQL執(zhí)行模塊之前,我們先了解MyBatis的整體架構(gòu),以及SQL執(zhí)行模塊在其中的核心地位。

1.1 SQL執(zhí)行模塊的核心職責(zé)

SQL執(zhí)行模塊主要承擔(dān)以下核心職責(zé):

1、SQL執(zhí)行:根據(jù)MappedStatement執(zhí)行SQL語句,并返回結(jié)果
2、緩存管理:管理一級緩存和二級緩存,提高查詢性能
3、事務(wù)管理:控制數(shù)據(jù)庫事務(wù)的提交、回滾和關(guān)閉
4、批處理支持:支持批量操作,提升數(shù)據(jù)修改效率
5、Statement管理:管理JDBC Statement對象的生命周期
6、插件攔截:提供攔截點(diǎn),支持插件擴(kuò)展

1.2 Executor接口體系

Executor是SQL執(zhí)行模塊的頂層接口,定義了SQL執(zhí)行的基本方法:

public interface Executor {
    // 執(zhí)行查詢(帶緩存Key)
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, CacheKey cacheKey, BoundSql boundSql);

    // 執(zhí)行查詢
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler);

    // 執(zhí)行更新(插入、更新、刪除)
    int update(MappedStatement ms, Object parameter);

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

    // 提交事務(wù)
    void commit(boolean required);

    // 回滾事務(wù)
    void rollback(boolean required);

    // 創(chuàng)建CacheKey
    CacheKey createCacheKey(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql);

    // 判斷是否緩存
    boolean isCached(MappedStatement ms, CacheKey cacheKey);

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

    // 獲取事務(wù)
    Transaction getTransaction();

    // 關(guān)閉執(zhí)行器
    void close(boolean forceRollback);
}

二、Executor執(zhí)行器架構(gòu)

MyBatis提供了多種Executor實(shí)現(xiàn),以適應(yīng)不同的使用場景。

2.1 Executor繼承體系

Executor采用了裝飾器模式,提供了靈活的功能擴(kuò)展:

Executor (接口)

├── BaseExecutor (抽象基類)

│   ├── SimpleExecutor (簡單執(zhí)行器)

│   ├── ReuseExecutor (可重用執(zhí)行器)

│   └── BatchExecutor (批處理執(zhí)行器)

└── CachingExecutor (緩存執(zhí)行器)

2.2 BaseExecutor抽象基類

BaseExecutor實(shí)現(xiàn)了Executor接口的大部分功能,定義了SQL執(zhí)行的基本流程:

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. 創(chuàng)建BoundSql
        BoundSql boundSql = ms.getBoundSql(parameter);

        // 2. 創(chuàng)建CacheKey
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

        // 3. 執(zhí)行查詢
        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;
        }

        // 執(zhí)行數(shù)據(jù)庫查詢
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        return list;
    }
}

2.3 SimpleExecutor簡單執(zhí)行器

SimpleExecutor是最基礎(chǔ)的執(zhí)行器實(shí)現(xiàn),每次執(zhí)行SQL都會創(chuàng)建新的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. 創(chuàng)建Configuration對象
            Configuration configuration = ms.getConfiguration();

            // 2. 創(chuàng)建StatementHandler
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter,
                                                                        rowBounds, resultHandler, boundSql);

            // 3. 創(chuàng)建Statement
            stmt = prepareStatement(handler, ms.getStatementLog());

            // 4. 執(zhí)行查詢
            return handler.<E>query(stmt, resultHandler);
        } finally {
            // 5. 關(guān)閉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可重用執(zhí)行器

ReuseExecutor會緩存Statement對象,相同SQL可以重用Statement,減少Statement創(chuàng)建開銷:

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) {
            // 緩存未命中,創(chuàng)建新的Statement
            stmt = prepareStatement(handler, statementLog);
            statementMap.put(sql, stmt);
        }
        return stmt;
    }
}

2.5 BatchExecutor批處理執(zhí)行器

BatchExecutor專門用于批量操作,會將多個(gè)SQL語句批量執(zhí)行:

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,復(fù)用Statement
            int last = statementList.size() - 1;
            stmt = statementList.get(last);
        } else {
            // 不同SQL,創(chuàng)建新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) {
                        // 執(zhí)行批處理
                        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緩存執(zhí)行器

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. 創(chuàng)建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執(zhí)行
        List<E> list = delegate.<E>query(ms, parameter, rowBounds, resultHandler, key, boundSql);

        // 3. 將結(jié)果放入二級緩存
        if (cache != null) {
            tcm.putObject(cache, key, list);
        }

        return list;
    }
}

三、SQL執(zhí)行流程

SQL的執(zhí)行流程是Executor的核心工作流程。

3.1 完整執(zhí)行流程

以查詢操作為例,完整的SQL執(zhí)行流程如下:

// 1. SqlSession調(diào)用Executor
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 1.1 獲取MappedStatement
        MappedStatement ms = configuration.getMappedStatement(statement);

        // 1.2 調(diào)用Executor執(zhí)行查詢
        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執(zhí)行查詢
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    // 2.1 獲取BoundSql
    BoundSql boundSql = ms.getBoundSql(parameter);

    // 2.2 創(chuàng)建CacheKey
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

    // 2.3 執(zhí)行查詢
    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 緩存未命中,查詢數(shù)據(jù)庫
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    return list;
}

// 4. 查詢數(shù)據(jù)庫
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                     ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
    List<E> list;

    // 4.1 占位緩存,處理循環(huán)依賴
    localCache.putObject(key, EXECUTION_PLACEHOLDER);

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

    // 4.4 將結(jié)果放入一級緩存
    localCache.putObject(key, list);

    // 4.5 處理延遲加載
    if (ms.getConfiguration().isLazyLoadingEnabled()) {
        if (deferredLoads != null && !deferredLoads.isEmpty()) {
            deferredLoads.clear();
        }
    }

    return list;
}

// 5. 執(zhí)行實(shí)際查詢
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                       ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

3.2 StatementHandler的作用

StatementHandler負(fù)責(zé)Statement的創(chuàng)建、參數(shù)設(shè)置和SQL執(zhí)行:

public interface StatementHandler {
    // 準(zhǔn)備Statement
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

    // 參數(shù)化Statement
    void parameterize(Statement statement) throws SQLException;

    // 執(zhí)行查詢
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

    // 執(zhí)行更新
    int update(Statement statement) throws SQLException;

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

    // 獲取BoundSql
    BoundSql getBoundSql();
}

3.3 ResultSetHandler的作用

ResultSetHandler負(fù)責(zé)將ResultSet映射為Java對象:

public interface ResultSetHandler {
    // 處理結(jié)果集
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;

    // 處理游標(biāo)結(jié)果集
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    // 處理輸出參數(shù)
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}

四、緩存管理機(jī)制

MyBatis提供了兩級緩存機(jī)制,有效提升查詢性能。

4.1 一級緩存(Local Cache)

一級緩存是SqlSession級別的緩存,默認(rèn)開啟,作用域是當(dāng)前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();
    }
}

一級緩存的特點(diǎn):

1、作用域:SqlSession級別
2、生命周期:與SqlSession相同,SqlSession關(guān)閉時(shí)緩存清空
3、緩存Key:由MappedStatement ID、參數(shù)SQL、分頁參數(shù)等組成
4、自動失效:執(zhí)行增刪改操作時(shí),一級緩存會自動清空

4.2 二級緩存(Global Cache)

二級緩存是Mapper級別的緩存,需要手動配置,作用域是Namespace:

<!-- 在Mapper XML中配置二級緩存 -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>

二級緩存的特點(diǎn):

1、作用域:Namespace(Mapper)級別
2、生命周期:應(yīng)用級別,直到應(yīng)用關(guān)閉
3、跨Session共享:多個(gè)SqlSession可以共享
4、配置靈活:可以自定義緩存策略

4.3 緩存Key的構(gòu)建

CacheKey由多個(gè)元素組成,確保緩存鍵的唯一性:

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

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

    // 2. 分頁參數(shù)
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());

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

    // 4. 參數(shù)值
    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使用裝飾器模式實(shí)現(xiàn)緩存功能的增強(qiáng):

// 基礎(chǔ)緩存
Cache cache = new PerpetualCache("myCache");

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

// 添加定時(shí)刷新
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);

    // 第一次查詢,訪問數(shù)據(jù)庫
    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第一次查詢,訪問數(shù)據(jù)庫
    User user1 = mapper1.selectById(1L);

    // session1提交,將數(shù)據(jù)寫入二級緩存
    session1.commit();

    // session2查詢,從二級緩存獲取
    User user2 = mapper2.selectById(1L);

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

五、事務(wù)管理

事務(wù)管理是數(shù)據(jù)庫操作的重要組成部分,Executor負(fù)責(zé)事務(wù)的創(chuàng)建、提交和回滾。

5.1 Transaction接口

Transaction是事務(wù)管理的頂層接口:

public interface Transaction {
    // 獲取數(shù)據(jù)庫連接
    Connection getConnection() throws SQLException;

    // 提交事務(wù)
    void commit() throws SQLException;

    // 回滾事務(wù)
    void rollback() throws SQLException;

    // 關(guān)閉連接
    void close() throws SQLException;

    // 獲取事務(wù)超時(shí)時(shí)間
    Integer getTimeout() throws SQLException;
}

5.2 事務(wù)隔離級別

MyBatis支持標(biāo)準(zhǔn)的事務(wù)隔離級別:

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 事務(wù)管理流程

Executor的事務(wù)管理流程:

// 提交事務(wù)
@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. 提交事務(wù)
    if (required) {
        transaction.commit();
    }

    return batchResults;
}

// 回滾事務(wù)
@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. 回滾事務(wù)
        if (required) {
            transaction.rollback();
        }
    } finally {
        if (required) {
            // 4. 關(guān)閉事務(wù)
            transaction.close();
        }
    }
}

5.4 自動提交與手動提交

// 自動提交模式
SqlSession session = sqlSessionFactory.openSession(true);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insert(user);
    // 無需手動提交,自動提交
} finally {
    session.close();
}

// 手動提交模式(默認(rèn))
SqlSession session = sqlSessionFactory.openSession();
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insert(user);
    // 需要手動提交
    session.commit();
} catch (Exception e) {
    // 異常時(shí)回滾
    session.rollback();
    throw e;
} finally {
    session.close();
}

5.5 Spring事務(wù)集成

在Spring環(huán)境中,通常使用Spring的事務(wù)管理:

@Service
@Transactional
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void updateUser(User user) {
        // Spring管理事務(wù),無需手動提交
        userMapper.update(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 轉(zhuǎn)賬操作:同一事務(wù)
        userMapper.decrease(fromId, amount);
        userMapper.increase(toId, amount);
    }
}

六、批處理機(jī)制

批處理可以顯著提升批量操作的性能。

6.1 批處理配置

使用批處理需要指定ExecutorType:

// 創(chuàng)建批處理SqlSession
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);

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

    // 刷新并執(zhí)行批處理
    session.flushStatements();

    // 提交事務(wù)
    session.commit();
} finally {
    session.close();
}

6.2 批處理原理

BatchExecutor的工作原理:

1、SQL緩存:相同SQL復(fù)用Statement
2、參數(shù)累積:多次調(diào)用addBatch()
3、批量執(zhí)行:調(diào)用executeBatch()
4、結(jié)果返回:返回每條SQL的執(zhí)行結(jié)果
@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;

    // 檢查是否可以復(fù)用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;
    }

    // 參數(shù)化并添加到批處理
    handler.parameterize(stmt);
    handler.batch(stmt);

    return BATCH_UPDATE_RETURN_VALUE;
}

6.3 批處理性能優(yōu)化

批處理的性能優(yōu)化建議:

1、合理控制批次大?。罕苊庖淮涡蕴峤贿^多SQL
2、使用BatchExecutor:批量操作時(shí)使用批處理執(zhí)行器
3、關(guān)閉自動提交:手動控制事務(wù)提交
4、合理使用flushStatements:控制批處理執(zhí)行時(shí)機(jī)
// 分批處理示例
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 批處理返回結(jié)果

批處理返回的是每條SQL影響的行數(shù):

List<BatchResult> results = session.flushStatements();

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

6.5 批處理注意事項(xiàng)

1.Statement限制:數(shù)據(jù)庫對PreparedStatement數(shù)量有限制
2.內(nèi)存占用:大量SQL會占用較多內(nèi)存
3.錯(cuò)誤處理:批處理中某條SQL失敗,需要特別處理
4.日志輸出:批處理日志可能較多,建議適當(dāng)調(diào)整日志級別

七、最佳實(shí)踐

7.1 Executor選擇建議

場景推薦Executor說明
一般查詢SIMPLE默認(rèn)選擇,每次創(chuàng)建新Statement
重復(fù)查詢多REUSE復(fù)用Statement,減少創(chuàng)建開銷
批量操作BATCH顯著提升批量操作性能
啟用二級緩存CACHING在其他Executor基礎(chǔ)上增加緩存

7.2 性能優(yōu)化建議

1、合理使用緩存:根據(jù)業(yè)務(wù)特點(diǎn)選擇緩存級別
2、批量操作優(yōu)化:大量數(shù)據(jù)修改使用BatchExecutor
3、及時(shí)清理緩存:避免緩存數(shù)據(jù)過期
4、控制事務(wù)范圍:事務(wù)盡量小,減少鎖競爭
5、使用連接池:避免頻繁創(chuàng)建連接

7.3 常見問題解決

問題1:一級緩存未生效

// 問題代碼
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1L);
User user2 = mapper.selectById(1L);
// user1 != user2,緩存未生效

// 原因:兩次查詢不在同一SqlSession
// 解決:確保在同一個(gè)SqlSession中查詢

問題2:二級緩存臟數(shù)據(jù)

<!-- 解決方案:設(shè)置刷新間隔 -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="false"/>

問題3:批處理內(nèi)存溢出

// 解決方案:分批處理
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();
}

八、總結(jié)

MyBatis的SQL執(zhí)行模塊是整個(gè)框架的核心執(zhí)行引擎,通過精心設(shè)計(jì)的Executor體系,實(shí)現(xiàn)了高效的SQL執(zhí)行、靈活的緩存管理、可靠的事務(wù)控制和強(qiáng)大的批處理能力。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot中的Redis?緩存問題及操作方法

    SpringBoot中的Redis?緩存問題及操作方法

    這篇文章主要介紹了SpringBoot中的Redis?緩存,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • 解決Java ClassNotFoundException異常的實(shí)踐步驟

    解決Java ClassNotFoundException異常的實(shí)踐步驟

    本文詳解Java中ClassNotFoundException的常見原因及解決方法,涵蓋類路徑配置、命名規(guī)范、打包問題、依賴管理、ClassLoader使用等,提供排查步驟和工具示例,感興趣的朋友跟隨小編一起學(xué)習(xí)吧
    2025-08-08
  • Java雙色球系統(tǒng)開發(fā)詳解

    Java雙色球系統(tǒng)開發(fā)詳解

    這篇文章主要為大家詳細(xì)介紹了Java雙色球系統(tǒng)的開發(fā),超級簡單的邏輯,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 字節(jié)二面SpringBoot可以同時(shí)處理多少請求

    字節(jié)二面SpringBoot可以同時(shí)處理多少請求

    這篇文章主要為大家介紹了字節(jié)二面之SpringBoot可以同時(shí)處理多少請求面試分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • SpringCloud Feign隔離與降級詳細(xì)分析

    SpringCloud Feign隔離與降級詳細(xì)分析

    Feign是Netflix公司開發(fā)的一個(gè)聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開發(fā)非?;A(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時(shí)出現(xiàn)的,而且配置也都非常相似
    2022-11-11
  • Java編寫迷宮小游戲

    Java編寫迷宮小游戲

    最近經(jīng)常在機(jī)房看同學(xué)在玩一個(gè)走迷宮的游戲,比較有趣,自己也用java寫一個(gè)實(shí)現(xiàn)隨機(jī)生成迷宮的算法,其實(shí)就是一個(gè)圖的深度優(yōu)先遍歷算法.
    2016-05-05
  • Java多線程編程中的并發(fā)安全問題及解決方法

    Java多線程編程中的并發(fā)安全問題及解決方法

    保障多線程并發(fā)安全,解決線程同步與鎖競爭問題,提高應(yīng)用性能與可靠性。多線程編程需要考慮線程安全性,使用同步機(jī)制保證共享變量的一致性,避免線程競爭導(dǎo)致的數(shù)據(jù)不一致與死鎖等問題。常用的同步機(jī)制包括synchronized、ReentrantLock、volatile等
    2023-04-04
  • mybatis于xml方式和注解方式實(shí)現(xiàn)多表查詢的操作方法

    mybatis于xml方式和注解方式實(shí)現(xiàn)多表查詢的操作方法

    在數(shù)據(jù)庫中,單表的操作是最簡單的,但是在實(shí)際業(yè)務(wù)中最少也有十幾張表,并且表與表之間常常相互間聯(lián)系,本文給大家介紹mybatis于xml方式和注解方式實(shí)現(xiàn)多表查詢的操作方法,感興趣的朋友一起看看吧
    2023-12-12
  • springboot?sleuth?日志跟蹤問題記錄

    springboot?sleuth?日志跟蹤問題記錄

    Spring?Cloud?Sleuth是一個(gè)在應(yīng)用中實(shí)現(xiàn)日志跟蹤的強(qiáng)有力的工具,使用Sleuth庫可以應(yīng)用于計(jì)劃任務(wù)?、多線程服務(wù)或復(fù)雜的Web請求,尤其是在一個(gè)由多個(gè)服務(wù)組成的系統(tǒng)中,這篇文章主要介紹了springboot?sleuth?日志跟蹤,需要的朋友可以參考下
    2023-07-07
  • Java 中ThreadLocal類詳解

    Java 中ThreadLocal類詳解

    什么是ThreadLocal?顧名思義它是local variable(線程局部變量)。它的功用非常簡單,就是為每一個(gè)使用該變量的線程都提供一個(gè)變量值的副本,是每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會和其它線程的副本沖突。從線程的角度看,就好像每一個(gè)線程都完全擁有該變量。
    2016-03-03

最新評論

资兴市| 大城县| 华池县| 宾川县| 边坝县| 桐梓县| 利津县| 饶平县| 龙山县| 武冈市| 泸西县| 昭觉县| 鲁山县| 襄垣县| 汶川县| 从江县| 枝江市| 辽宁省| 庆城县| 龙海市| 教育| 安化县| 长治县| 新源县| 桂林市| 建德市| 威远县| 资讯 | 西乌| 通城县| 新泰市| 武威市| 分宜县| 和平县| 开鲁县| 隆安县| 论坛| 调兵山市| 九寨沟县| 定南县| 财经|