Mybatis Plus批量更新数据不忽略null值,在不修改全局配置的情况下

此文章记录的内容是如何在**不影响全局null值忽略配置**的情况下,如何使用**mybatisplus**进行**批量更新**
如果要在全局生效的话直接搜索MybatisPlus的null值忽略配置。

MyService.java#updateBatchWithNull

@Service
public class MyService extends MPJBaseServiceImpl<TestMapper, TestEntity> {
    public boolean updateBatchWithNull(Long groupId, List<TestEntity> contents, int batchSize) {
     	// updateWithNull是TestMapper的方法
        String sqlStatement = this.currentMapperClass().getName() + ".updateWithNull";
        return executeBatch(contents, batchSize, (sqlSession, entity) -> {
            entity.setGroupId(id);
            sqlSession.update(sqlStatement, entity);
        });
    }

    // 调用示例
    @Transactional(rollbackFor = Exception.class)
    public boolean saveByUser(Long groupId, List<TestEntity> list, Long userId){
        // TODO 群管理员权限校验
        // TODO 分离需要新增和更新的列表
        List<TestEntity> insertList = null;
        List<TestEntity> updateList = null;
        // TODO 其他业务逻辑
        if(CollectionUtils.isNotEmpty(insertList)){
            saveBatch(insertList, 50);
        }
        if(CollectionUtils.isNotEmpty(updateList)){
            updateBatchWithNull(insertList, 50);
        }
        return true;
    }
    
}

TestMapper.java

int updateWithNull(TestEntity content);

TestMapper.xml

<!-- 批量更新包含null值的字段 -->
<update id="updateWithNull">
    UPDATE my_table_name
    <set>
        <if test="groupId != null">group_id = #{groupId},</if>
        <if test="name != null and name != ''">name = #{name,jdbcType=VARCHAR},</if>
        <!--数据库里会被设置为null-->
        play_content = #{playContent,jdbcType=VARCHAR},
        hint_content = #{hintContent,jdbcType=VARCHAR},
        <if test="updateBy != null">update_by = #{updateBy,jdbcType=VARCHAR},</if>
        update_time = NOW()
    </set>
    WHERE id = #{id} and group_id = #{groupId} and del_flag = '0'
</update>

源码解析

// com.baomidou.mybatisplus.extension.toolkit.SqlHelper#executeBatch
public static <E> boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
    Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
    return !CollectionUtils.isEmpty(list) && executeBatch(sqlSessionFactory, log, sqlSession -> {
        int size = list.size();
        int idxLimit = Math.min(batchSize, size);
        int i = 1;
        for (E element : list) {
            consumer.accept(sqlSession, element);
            if (i == idxLimit) {
                sqlSession.flushStatements();
                idxLimit = Math.min(idxLimit + batchSize, size);
            }
            i++;
        }
    });
}
// com.baomidou.mybatisplus.extension.toolkit.SqlHelper#executeBatch
public static boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Consumer<SqlSession> consumer) {
    SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
    boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
    if (sqlSessionHolder != null) {
        SqlSession sqlSession = sqlSessionHolder.getSqlSession();
        //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
        //按道理来说,这里的值应该一直为false。
        sqlSession.commit(!transaction);
    }
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    if (!transaction) {
        log.warn("SqlSession [" + sqlSession + "] Transaction not enabled");
    }
    try {
        consumer.accept(sqlSession);
        //非事务情况下,强制commit。
        sqlSession.commit(!transaction);
        return true;
    } catch (Throwable t) {
        sqlSession.rollback();
        Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
        if (unwrapped instanceof PersistenceException) {
            MyBatisExceptionTranslator myBatisExceptionTranslator
            = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
            Throwable throwable = myBatisExceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
            if (throwable != null) {
                throw throwable;
            }
        }
        throw ExceptionUtils.mpe(unwrapped);
    } finally {
        sqlSession.close();
    }
}
// org.apache.ibatis.executor.BatchExecutor#doFlushStatements
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<>();
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        BatchResult batchResult = batchResultList.get(i);
        try {
          batchResult.setUpdateCounts(stmt.executeBatch());
          MappedStatement ms = batchResult.getMappedStatement();
          List<Object> parameterObjects = batchResult.getParameterObjects();
          KeyGenerator keyGenerator = ms.getKeyGenerator();
          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
          } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue #141
            for (Object parameter : parameterObjects) {
              keyGenerator.processAfter(this, ms, stmt, parameter);
            }
          }
          // Close statement to close cursor #1109
          closeStatement(stmt);
        } catch (BatchUpdateException e) {
          StringBuilder message = new StringBuilder();
          message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1).append(")")
              .append(" failed.");
          if (i > 0) {
            message.append(" ").append(i)
                .append(" prior sub executor(s) completed successfully, but will be rolled back.");
          }
          throw new BatchExecutorException(message.toString(), e, results, batchResult);
        }
        results.add(batchResult);
      }
      return results;
    } finally {
      for (Statement stmt : statementList) {
        closeStatement(stmt);
      }
      currentSql = null;
      statementList.clear();
      batchResultList.clear();
    }
}

参考内容

原来SqlSession只是个甩手掌柜?真正干活的是谁?_sqlsession是方法级别的-CSDN博客