You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mybatis-Spring批量插入异常:未调用flush却自动执行

MyBatis批量插入未按预期延迟执行问题

问题现象

使用MyBatis实现批量插入时,循环调用batchMap.insert()后,每条插入语句在未手动调用flush或commit的情况下就被即时执行,日志显示每次插入操作后都触发了executeBatch。

相关配置(applicationContext.xml)

<bean id="refDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName">
    <value>com.mysql.cj.jdbc.Driver</value>
  </property>
  <property name="url">
    <value>jdbc:mysql://localhost:3306/somedatabase?rewriteBatchedStatements=true</value>
  </property>
  <property name="username"><value>root</value></property>
  <property name="password"><value>root</value></property>
</bean>
<bean id="refSqlMapClientIbatis" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="configLocation" value="/WEB-INF/jsp/ref/sql-map-config.xml" />
  <property name="dataSource" ref="refDataSource" />
</bean>

<bean id="refSqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="refSqlMapClientIbatis" />
</bean>
<bean id="refSqlMapClient" class="mypackage.base.IbatisSqlMapClient">
  <property name="sqlSession" ref="refSqlSession" />
</bean>

<bean id="refBatchSqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="refSqlMapClientIbatis" />
  <constructor-arg index="1" value="BATCH" />
</bean>
<bean id="refBatchSqlMapClient" class="mypackage.base.MybatisSqlSession">
  <property name="batchSqlSession" ref="refBatchSqlSession" />
</bean>

<bean id="RefBaseDAO" abstract="true">
  <property name="sqlMap" ref="refSqlMapClient"/>
  <property name="batchMap" ref="refBatchSqlMapClient"/>
</bean>

日志信息

java.sql.PreparedStatement.addBatch: INSERT INTO  fz_ref_um_rates
        (um_rate_value, um_id, to_um_id, relation_between_prod_spec, um_rate_product_id, um_rate_product_assigned_spec_id, brch_id, created_by, create_date)
        VALUES
        (2.0, '952', '391', N, null, null, '284', 2, NOW());

[09/12/2022 11:48:52] org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
 [INFO]: java.sql.Statement.executeBatch: 

[09/12/2022 11:48:52] org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
 [INFO]: java.sql.PreparedStatement.addBatch: INSERT INTO  fz_ref_um_rates
        (um_rate_value, um_id, to_um_id, relation_between_prod_spec, um_rate_product_id, um_rate_product_assigned_spec_id, brch_id, created_by, create_date)
        VALUES
        (3.0, '952', '339', N, null, null, '284', 2, NOW());

[09/12/2022 11:48:52] org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
 [INFO]: java.sql.Statement.executeBatch: 

[09/12/2022 11:48:52] org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
 [INFO]: java.sql.PreparedStatement.addBatch: INSERT INTO  fz_ref_um_rates
        (um_rate_value, um_id, to_um_id, relation_between_prod_spec, um_rate_product_id, um_rate_product_assigned_spec_id, brch_id, created_by, create_date)
        VALUES
        (45.0, '952', '338', N, null, null, '284', 2, NOW());

[09/12/2022 11:48:52] org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
 [INFO]: java.sql.Statement.executeBatch: 

自定义MybatisSqlSession类代码

public class MybatisSqlSession {
    private SqlSession batchSqlSession = null;

    public Object insert(String string, Object object) throws FZDAOException {
        try {
            System.out.println("insert called");
            return batchSqlSession.insert(string, object);

        } catch (SqlSessionException ex) {
            throw new MyException(ex.getMessage(), ex);
        }
    }
}

批量插入调用代码

public void insertUmRates(List<UmRatesVO> addList) throws FZDAOException
{
    for(UmRatesVO umRatesVO : addList)
    {
        batchMap.insert("UmRatesQueries.insertUmRates", umRatesVO);
    }
}

解决方案

1. 手动触发批处理执行与提交

MyBATS的BATCH模式下,insert()仅将语句加入队列,但如果未在事务管理范围内,可能会自动执行。需在循环结束后手动调用flush和commit:

方式一:修改调用代码

public void insertUmRates(List<UmRatesVO> addList) throws FZDAOException
{
    try {
        for(UmRatesVO umRatesVO : addList)
        {
            batchMap.insert("UmRatesQueries.insertUmRates", umRatesVO);
        }
        // 手动触发批处理执行
        ((MybatisSqlSession) batchMap).getBatchSqlSession().flushStatements();
        // 提交事务
        ((MybatisSqlSession) batchMap).getBatchSqlSession().commit();
    } catch (Exception e) {
        ((MybatisSqlSession) batchMap).getBatchSqlSession().rollback();
        throw new FZDAOException(e.getMessage(), e);
    }
}

方式二:扩展MybatisSqlSession类

在MybatisSqlSession中添加flush、commit、rollback方法:

public void flush() {
    batchSqlSession.flushStatements();
}

public void commit() {
    batchSqlSession.commit();
}

public void rollback() {
    batchSqlSession.rollback();
}

// 新增getter方法
public SqlSession getBatchSqlSession() {
    return batchSqlSession;
}

调用时:

public void insertUmRates(List<UmRatesVO> addList) throws FZDAOException
{
    try {
        for(UmRatesVO umRatesVO : addList)
        {
            batchMap.insert("UmRatesQueries.insertUmRates", umRatesVO);
        }
        batchMap.flush();
        batchMap.commit();
    } catch (Exception e) {
        batchMap.rollback();
        throw new FZDAOException(e.getMessage(), e);
    }
}

2. 配置事务管理

确保批量插入方法被Spring事务管控(添加@Transactional注解),事务会统一控制提交时机,避免单条语句自动执行。

3. 禁用MyBatis自动提交

在sql-map-config.xml中确认关闭自动提交:

<settings>
    <setting name="autoCommit" value="false"/>
</settings>

4. 使用MyBatis原生批量插入语法(更优方案)

直接通过foreach标签实现单条SQL批量插入,性能更高且避免批处理模式的自动执行问题:

Mapper XML配置

<insert id="insertUmRatesBatch" parameterType="java.util.List">
    INSERT INTO fz_ref_um_rates
    (um_rate_value, um_id, to_um_id, relation_between_prod_spec, um_rate_product_id, um_rate_product_assigned_spec_id, brch_id, created_by, create_date)
    VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.umRateValue}, #{item.umId}, #{item.toUmId}, #{item.relationBetweenProdSpec}, #{item.umRateProductId}, #{item.umRateProductAssignedSpecId}, #{item.brchId}, #{item.createdBy}, NOW())
    </foreach>
</insert>

调用代码

public void insertUmRates(List<UmRatesVO> addList) throws FZDAOException
{
    try {
        batchMap.insert("UmRatesQueries.insertUmRatesBatch", addList);
        batchMap.flush();
        batchMap.commit();
    } catch (Exception e) {
        batchMap.rollback();
        throw new FZDAOException(e.getMessage(), e);
    }
}

内容的提问来源于stack exchange,提问作者Dany Allaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:55:20