mybatis-plus 扩展批量新增的实现

2023-01-09 15:07:05

目录前言一、MP如何扩展批量新增方法?二、实现步骤1.扩展批插类2.编写sql注入器3.spring容器中实例化sql注入器总结前言最近发现公司的微服务项目中没有统一的批量新增方法,公司用的是MP插件...

目录
前言
一、MP如何扩展批量新增方法?
二、实现步骤
1.扩展批插类
2.编写sql注入器
3.spring容器中实例化sql注入器
总结

前言

最近发现公司的微服务项目中没有统一的批量新增方法,公司用的是MP插件,遇到批量新增都是单独去去编写XML实现,费时费力,而MP自带的批插方法只是实现了分批条sql,跟真正意义上一条sql实现批插还是有很大的性能差异,所以决定实现一个统一的批插方法。

一、MP如何扩展批量新增方法?

MP给我们预留了一个可以真正实现批插的插件InsertBATchSomeColumn,但是只是针对mysql数据库的,我们可以参照它稍加改造扩展一个针对oracle或者其他数据库的批插插件类,然后通过sql注入器的方式注入并初始化,就可以实现真正的批插了。

二、实现步骤

1.扩展批插类

代码如下(示例):

public class InsertBatchSomeColumnOracle extends AbstractMethod {

  /**
  * 字段筛选条件
  */
  @Setter
  @Accessors(chain = true)
  private Predicate<TableFieldInfo> predicate;

  @SuppressWarnings("Duplicates")
  @Override
  public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    KeyGenerator keyGenerator = new NoKeyGenerator();
    SqlMethod sqlMethod = SqlMethod.INSERT_ALL;
    List<TableFieldInfo> fieldList = tableInfo.getFieldList();
    String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) +
        this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
    String tableName = tableInfo.getTableName();
    String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
    String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(ENTITY_DOT, false) +
        this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
    insertSqlProperty = " into " + tableName + columnScript + " values " + LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
    String batchScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, EMPTY);
    String keyProperty = null;
    String keyColumn = null;
    // 表包含主键处理逻辑,如果不包含主键当普通字段处理
    if (tableInfo.havePK()) {
      if (tableInfo.getIdType() == IdType.AUTO) {
        /* 自增主键 */
        keyGenerator = new Jdbc3KeyGenerator();
        keyProperty = tableInfo.getKeyProperty();
        keyColumn = tableInfo.getKeyColumn();
      } else {
        if (null != tableInfo.getKeySequence()) {
          keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(), tableInfo, builderAssistant);
          keyProperty = tableInfo.getKeyProperty();
          keyColumn = tableInfo.getKeyColumn();
        }
      }
    }
    String sql = String.format(sqlMethod.getSql(), batchScript);
    SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(), sqlSource, keyGenerator, keyProperty, keyColumn);
  }

  public String getMethod() {
    // 自定义 mapper 方法名
    return "insertBatchSomeColumnOracle";
  }
}

2.编写sql注入器

代码如下(示例):

public class MpSqlInjector extends DefaultSqlInjector {

  @Override
  public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
    // 防止父类的方法不可使用
    List<AbstractMethod> methodList = super.getMethodList(mapperClass);
    // 添加批量插入的方法
    methodList.add(new InsertBatchSomeColumn());
    methodList.add(new InsertBatchSomeColumnOracle());
    return methodList;
  }
}

3.spring容器中实例化sql注入器

代码如下(示例):

@Bean
public MpSqlInjector mpSqlInjector() {
  return new MpSqlInjector();
}

4.业务代码实现

代码如下(示例):

public interface MyBaseMapper<T> extends BaseMapper<T> {

  /**
  * 批量插入 (mysql)
  * @param entityList 实体列表
  * @return 影响行数
  */
  int insertBatchSomeColumn(Collection<T> entityList);

  /**
  * 批量插入 (oracle)
  * @param entityList 实体列表
  * @return 影响行数
  */
  int insertBatchSomeColumnOracle(Collection<T> entityList);
}


/**
  * 批量插入记录(选择字段,策略插入)
  *
  * @param entityList 实体对象集合
  */
  public void saveBatch(Collection<T> entityList) {
    if (DEFAULT_MUST_SAVE_BATCH) {
      ServiceAssert.isTrue(CollectionUtil.isNotEmpty(entityList), "批量插入传入空集合, 插入失败");
    }
    if (CollectionUtil.isEmpty(entityList)) {
      return;
    }
    int res;
    if (DATABASE_TYPE_ORACLE.equalsIgnoreCase(dbType)) {
      res = getBaseMapper().insertBatchSomeColumnOracle(entityList);
    } else {
      res = getBaseMapper().insertBatchSomeColumn(entityList);
    }
    ServiceAssert.isTrue(res == entityList.size(), "插入失败,请联系管理员");
  }
  /**
  * 批量插入记录(选择字段,策略插入)
  *
  * @param entityList 实体对象集合
  * @param batchSize 每批次插入数
  */
  public void saveBatch(Collection<T> entityList, int batchSize) {
    if (DEFAULT_MUST_SAVE_BATCH) {
      ServiceAssert.isTrue(CollectionUtil.isNotEmpty(entityList), "批量插入传入空集合, 插入失败");
    }
    if (CollectionUtil.isEmpty(entityList)) {
      return;
    }
    int size = entityList.size();
    int idxLimit = Math.min(batchSize, size);
    int i = 1;
    //保存单批提交的数据集合
    List<T> batchList = new ArrayList<>();
    for(Iterator<T> it = entityList.iterator(); it.hasNext(); ++i) {
      T element = it.next();
      batchList.add(element);
      int res;
      if (i == idxLimit) {
        if (DATABASE_TYPE_ORACLE.equalsIgnoreCase(dbType)) {
          res = getBaseMapper().insertBatchSomeColumnOracle(batchList);
        } else {
          res = getBaseMapper().insertBatchSomeColumn(batchList);
        }
        //每次提交后需要清空集合数据
        batchList.clear();
        idxLimit = Math.min(idxLimit + batchSize, size);
        ServiceAssert.isTrue(res == entityList.size(), "插入失败,请联系管理员");
      }
    }
  }

总结

遇到问题可以看看源码,说不定作者已经给你留好路了。

到此这篇关于mybatis-plus 扩展批量新增的实现的文章就介绍到这了,更多相关mybatis-plus 批量新增内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!