mybatis-plus 扩展批量新增的实现
JOEY P 人气:0前言
最近发现公司的微服务项目中没有统一的批量新增方法,公司用的是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(), "插入失败,请联系管理员"); } } }
总结
遇到问题可以看看源码,说不定作者已经给你留好路了。
加载全部内容