Mybatis Plus用自定义新增,来实现真正的批量新增方式!

MyBatis Plus批量新增与更新

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

Mybatis plus 的批量插入方法saveBatch或者批量更新方法updateBatch,批量新增其实它是一个伪批量新增方法和更新方法,打印SQL日志的时候,我们可以发现它还是一条条执行insert语句的。今天带大家在 Mybatis Plus 中,实现 MySQL 真实的批量新增,即insert(…) values(xx,x),(xx,x);而不是伪批量新增

一、mysql的批量新增是什么?

MySQL 支持一条 SQL 语句可以批量插入多条记录,格式如下:
INSERT INTO t_user (name, age, gender) VALUES (‘xx’, 0, 1), (‘yy’, 0, 1), (‘zz’, 0, 1);
和常规的 INSERT 语句不同的是,VALUES 支持多条记录,通过 , 逗号隔开。这样,可以实现一次性插入多条记录。
数据量不多的情况下,常规 INSERT 和批量插入性能差距不大,但是,一旦数量级上去后,执行耗时差距就拉开了,
使用这种新增方式要比一条条的插入快很多。

二、使用步骤

先新建一个Spring boot项目,完成基本的相关配置,项目的基本结构:
在这里插入图片描述

1.引入mybatisplus的pom依赖,该案例使用的mybatis plus版本为3.5.7

       <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- mybatis-plus 依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!-- mybatis-plus 代码生成器依赖 -->
        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <!-- freemarker 依赖,用于代码生成 -->
        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

2.Spring Boot的yml配置

在这里插入图片描述

3.编写自定义insert和update的SQL 批量处理方法

insert
package com.example.ftserver.plugin;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.List;
import java.util.function.Predicate;


/**
 * @author aaa
 * @date 2023-09-27 18:27
 * @description
 */
public class InsertBatchColumn extends AbstractMethod {

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

    public InsertBatchColumn() {
        super("insertBatch");
    }

    public InsertBatchColumn(Predicate<TableFieldInfo> predicate) {
    	// 此处的名称必须与后续的RootMapper的新增方法名称一致
        super("insertBatch");
        this.predicate = predicate;
    }

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true,false) +
                this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true,ENTITY_DOT, false) +
                this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
        insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
        String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (tableInfo.havePK()) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主键 */
                keyGenerator = Jdbc3KeyGenerator.INSTANCE;
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(modelClass.getName(), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
		// 注意第三个参数,需要与后续的RootMapper里面新增方法名称要一致,不然会报无法绑定异常
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, keyGenerator, keyProperty, keyColumn);

    }
}

update

package com.example.ftserver.plugin;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;


/**
 * @author aaa
 * @date 2023-09
 * @description
 */
public class UpdateBatchColumn extends AbstractMethod {

    /**
     * @param methodName 方法名
     * @since 3.5.0
     */
    public UpdateBatchColumn(String methodName) {
        super(methodName);
    }

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = "<script>\n<foreach collection=\"list\" item=\"item\" separator=\";\">\nupdate %s %s where %s=#{%s} %s\n</foreach>\n</script>";
        String additional = tableInfo.isWithVersion() ? tableInfo.getVersionFieldInfo().getVersionOli("item", "item.") : "" + tableInfo.getLogicDeleteSql(true, true);
        String setSql = sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, "item", "item.");
        String sqlResult = String.format(sql, tableInfo.getTableName(), setSql, tableInfo.getKeyColumn(), "item." + tableInfo.getKeyProperty(), additional);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三个参数必须和RootMapper的自定义方法名一致
        return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);


    }


}

4. 创建一个 SQL 注入器 InsertBatchSqlInjector

package com.example.ftserver.config;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.example.ftserver.plugin.InsertBatchColumn;
import com.example.ftserver.plugin.UpdateBatchColumn;

import java.util.List;

/**
 * @author aaa
 * @description
 */
public class CustomizedSqlInjector extends DefaultSqlInjector {

	// 3.5.7中,该方法已经被废弃,使用新的getMethodList方法
   /* public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methods = super.getMethodList(mapperClass,tableInfo);
        // 自定义的insert SQL注入器
        methods.add(new InsertBatchColumn());
        // 自定义的update SQL注入器,参数需要与RootMapper的批量update名称一致
        methods.add(new UpdateBatchColumn("updateBatch")); 
        return methods;
    }*/
       public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methods = super.getMethodList(configuration, mapperClass, tableInfo);
        // 自定义的insert SQL注入器
        methods.add(new InsertBatchColumn());
        // 自定义的update SQL注入器,参数需要与RootMapper的批量update名称一致
        methods.add(new UpdateBatchColumn("updateBatch"));
        return methods;
    }


}

5.将SQL 注入器放入配置类

package com.example.ftserver.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author aaa
 * @description
 */
@Configuration
@MapperScan(basePackages = "com.example.ftserver.mapper") // 不用配置.*
public class MybatisPlusConfig {

    @Bean
    public CustomizedSqlInjector sqlInjector(){
        return new CustomizedSqlInjector();
    }
    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        return interceptor;
    }
}

6.新建一个在 config 包下创建 RootMapper 接口,让其继承自 Mybatis Plus 提供的 BaseMapper, 并定义批量插入方法。注意:BaseMapper中的所有方法需要重写,不然调用时可能会报空指针异常

package com.example.ftserver.plugin;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.Collection;

/**
 * @author aaa
 * @date 2023-09-28 16:11
 * @description 
 */
public interface RootMapper<T> extends BaseMapper<T> {
	/**
     * 自定义批量新增,代替mybatis plus 自带的批量新增
     *
     * @param batchList 批量新增参数
     * @return int
     */
    int insertBatch(@Param("list") Collection<T> batchList);

    /**
     * 根据id批量修改
     *
     * @param batchList 批量更新参数
     * @return int
     */
    int updateBatch(@Param("list") Collection<T> batchList);

    @Override
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    @Override
    default int deleteById(Serializable id) {
        return deleteById(id, true);
    }

    /**
     * 根据 ID 删除
     *
     * @param useFill 是否填充
     * @param obj     主键ID或实体
     * @since 3.5.7
     */
    @Override
    default int deleteById(Object obj, boolean useFill) {
        return BaseMapper.super.deleteById(obj, useFill);
    }

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体对象
     * @since 3.4.4
     */
    @Override
    int deleteById(T entity);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    @Override
    default int deleteByMap(Map<String, Object> columnMap) {
        return this.delete(Wrappers.<T>query().allEq(columnMap));
    }

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    @Override
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);


    /**
     * 删除(根据ID或实体 批量删除)
     *
     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
     * @since 3.5.7
     */
    @Override
    default int deleteByIds(@Param(Constants.COLL) Collection<?> idList) {
        return deleteByIds(idList, true);
    }

    /**
     * 删除(根据ID或实体 批量删除)
     * <p>
     * 普通删除: DELETE FROM h2user WHERE id IN ( ? , ? )
     * </p>
     * <p>
     * 逻辑删除: UPDATE h2user SET deleted=1 WHERE id IN ( ? , ? ) AND deleted=0
     * </p>
     * <p>
     * 逻辑删除(填充): UPDATE h2user SET delete_user = 'xxx', deleted=1 WHERE id IN ( ? , ? ) AND deleted=0
     *     <ul>注意:无论参数为id还是实体,填充参数只会以方法追加的et参数为准.<ul/>
     * </p>
     *
     * @param collections 主键ID列表或实体列表(不能为 null 以及 empty)
     * @param useFill     逻辑删除下是否填充
     * @since 3.5.7
     */
    @Override
    default int deleteByIds(@Param(Constants.COLL) Collection<?> collections, boolean useFill) {
        return BaseMapper.super.deleteByIds(collections, useFill);
    }

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    @Override
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null,当entity为null时,无法进行自动填充)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    @Override
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 Wrapper 更新记录
     * <p>此方法无法进行自动填充,如需自动填充请使用{@link #update(Object, Wrapper)}</p>
     *
     * @param updateWrapper {@link UpdateWrapper} or {@link LambdaUpdateWrapper}
     * @since 3.5.4
     */
    @Override
    default int update(@Param(Constants.WRAPPER) Wrapper<T> updateWrapper) {
        return update(null, updateWrapper);
    }

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    @Override
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    @Override
    List<T> selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList        idList 主键ID列表(不能为 null 以及 empty)
     * @param resultHandler resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    @Override
    void selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList, ResultHandler<T> resultHandler);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    @Override
    default List<T> selectByMap(Map<String, Object> columnMap) {
        return this.selectList(Wrappers.<T>query().allEq(columnMap));
    }

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap     表字段 map 对象
     * @param resultHandler resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    default void selectByMap(Map<String, Object> columnMap, ResultHandler<T> resultHandler) {
        this.selectList(Wrappers.<T>query().allEq(columnMap), resultHandler);
    }

    /**
     * 根据 entity 条件,查询一条记录
     * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
        return this.selectOne(queryWrapper, true);
    }

    /**
     * 根据 entity 条件,查询一条记录,现在会根据{@code throwEx}参数判断是否抛出异常,如果为false就直接返回一条数据
     * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @param throwEx      boolean 参数,为true如果存在多个结果直接抛出异常
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, boolean throwEx) {
        return BaseMapper.super.selectOne(queryWrapper, true);
    }

    /**
     * 根据 Wrapper 条件,判断是否存在记录
     *
     * @param queryWrapper 实体对象封装操作类
     * @return 是否存在记录
     */
    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0;
    }

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper  实体对象封装操作类(可以为 null)
     * @param resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    void selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @since 3.5.3.2
     */
    List<T> selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page          分页查询条件
     * @param queryWrapper  实体对象封装操作类(可以为 null)
     * @param resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    void selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);


    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper  实体对象封装操作类
     * @param resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    void selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     * @since 3.5.3.2
     */
    List<Map<String, Object>> selectMaps(IPage<? extends Map<String, Object>> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page          分页查询条件
     * @param queryWrapper  实体对象封装操作类
     * @param resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    void selectMaps(IPage<? extends Map<String, Object>> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <E> List<E> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper  实体对象封装操作类(可以为 null)
     * @param resultHandler 结果处理器 {@link ResultHandler}
     * @since 3.5.4
     */
    <E> void selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<E> resultHandler);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    default <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
        return BaseMapper.super.selectPage(page, queryWrapper);
    }

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    default <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
        return BaseMapper.super.selectMapsPage(page, queryWrapper);
    }

    /**
     * 主键存在更新记录,否插入一条记录
     *
     * @param entity 实体对象 (不能为空)
     * @since 3.5.7
     */
    default boolean insertOrUpdate(T entity) {
        return BaseMapper.super.insertOrUpdate(entity);
    }


    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @since 3.5.7
     */
    default List<BatchResult> insert(Collection<T> entityList) {
        return insert(entityList, Constants.DEFAULT_BATCH_SIZE);
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     * @since 3.5.7
     */
    default List<BatchResult> insert(Collection<T> entityList, int batchSize) {
        return BaseMapper.super.insert(entityList, batchSize);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @since 3.5.7
     */
    default List<BatchResult> updateById(Collection<T> entityList) {
        return updateById(entityList, Constants.DEFAULT_BATCH_SIZE);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     * @since 3.5.7
     */
    default List<BatchResult> updateById(Collection<T> entityList, int batchSize) {
        return BaseMapper.super.updateById(entityList, batchSize);
    }

    /**
     * 批量修改或插入
     *
     * @param entityList 实体对象集合
     * @since 3.5.7
     */
    default List<BatchResult> insertOrUpdate(Collection<T> entityList) {
        return insertOrUpdate(entityList, Constants.DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量修改或插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     * @since 3.5.7
     */
    default List<BatchResult> insertOrUpdate(Collection<T> entityList, int batchSize) {
        return BaseMapper.super.insertOrUpdate(entityList, batchSize);
    }

    /**
     * 批量修改或插入
     *
     * @param entityList 实体对象集合
     * @since 3.5.7
     */
    default List<BatchResult> insertOrUpdate(Collection<T> entityList, BiPredicate<BatchSqlSession, T> insertPredicate) {
        return insertOrUpdate(entityList, insertPredicate, Constants.DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量修改或插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     * @since 3.5.7
     */
    default List<BatchResult> insertOrUpdate(Collection<T> entityList, BiPredicate<BatchSqlSession, T> insertPredicate, int batchSize) {
        return BaseMapper.super.insertOrUpdate(entityList, insertPredicate, batchSize);
    }
}

7.新建 自己的Mapper,注意继承刚刚自定义的 RootMapper, 而不是Mybatis plus的 BaseMapper :

package com.example.ftserver.mapper;

import com.test.ft.common.entity.PersonEntity;
import com.example.ftserver.plugin.RootMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author aaa
 * @since 2023-10-19 16:32:39
 */
public interface PersonMapper extends RootMapper<PersonEntity> {

}

批量测试结果

自增id示例:

package com.test.ft.common.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.test.ft.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author aaa
 * @since 2023-10-19 16:32:39
 */
@Getter
@Setter
@Accessors(chain = true)
@TableName("person_t")
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "PersonEntity对象", description = "")
public class PersonEntity extends BaseEntity {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty("姓名")
    @TableField("`name`")
    private String name;

    @ApiModelProperty("年龄")
    @TableField("age")
    private Integer age;
}

id为UUID示例:

package com.test.ft.common.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.test.ft.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author tx wb
 * @since 2023-10-19 16:32:38
 */
@Getter
@Setter
@Accessors(chain = true)
@TableName("company_t")
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "CompanyEntity对象", description = "")
public class CompanyEntity extends BaseEntity {

    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String id;

    @TableField("company_name")
    private String companyName;

    @TableField("address")
    private String address;
}

测试示例:

package com.example.ftserver;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.ftserver.mapper.CompanyMapper;
import com.example.ftserver.mapper.PersonMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.test.ft.common.entity.CompanyEntity;
import com.test.ft.common.entity.PersonEntity;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@SpringBootTest
@RunWith(SpringRunner.class)
class FtServerApplicationTests {
    @Autowired
    RestHighLevelClient client;

    @Autowired
    CompanyMapper companyMapper;
    @Autowired
    PersonMapper personMapper;

 /**
  * 批量新增测试
  */
    @Test
    public void testBatch() {
        List<PersonEntity> personEntityList = Lists.newArrayList();
        for (int i = 0; i < 5; i++) {
            PersonEntity entity = new PersonEntity();
            entity.setAge(i + 1);
            String string = RandomUtil.randomString(i + 1);
            if (string.length() > 10) {
                string = string.substring(0, 9);
            }
            entity.setName(string);
            personEntityList.add(entity);
        }

        List<CompanyEntity> companyEntityList = Lists.newArrayList();
        for (int i = 0; i < 5; i++) {
            CompanyEntity entity = new CompanyEntity();
            String string = RandomUtil.randomString(i + 1);
            if (string.length() > 10) {
                string = string.substring(0, 9);
            }
            entity.setCompanyName(string);
            string = RandomUtil.randomString(i + 1);
            if (string.length() > 1000) {
                string = string.substring(500, 800);
            }
            entity.setAddress(string);
            companyEntityList.add(entity);
        }
        // id为非自增
        companyMapper.insertBatch(companyEntityList);
        // id为自增
        personMapper.insertBatch(personEntityList);


    }

}
 /**
  * 批量更新测试
  */
 @Test
    public void updateBatchTest() {
        List<CompanyEntity> list = companyMapper.selectList(Wrappers.lambdaQuery(CompanyEntity.class));

        for (CompanyEntity company : list) {
            String address="cc"+RandomUtil.randomNumbers(20);
            if (address.length()>1000) {
                address=address.substring(500);

            }
            company.setAddress(address);
        }
        companyMapper.updateBatch(list);

    }

测试结果:

insert测试结果:
在这里插入图片描述
update测试结果:
在这里插入图片描述
可以看到,已经insert和update成功

附录mybatis plus代码生成器:

可以将生成器直接放置在test目录下:
在这里插入图片描述
代码:

package com.example.ftserver;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import com.example.ftserver.plugin.RootMapper;
import com.test.ft.common.entity.BaseEntity;

import java.util.Collections;

/**
 * @author aaa
 * @date 2023-10-19 15:10
 * @description
 */
public class Generator {
    public static void main(String[] args) {
        DataSourceConfig.Builder dataSourceConfigBuilder = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/test?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false",
                "root", "root").dbQuery(new MySqlQuery()).typeConvert(new MySqlTypeConvert()).keyWordsHandler(new MySqlKeyWordsHandler());
        FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(dataSourceConfigBuilder);
        fastAutoGenerator.globalConfig(builder ->
                builder.disableOpenDir().outputDir("F:\\xx\\ft-server" + "/src/main/java").author("tb").commentDate("yyyy-MM-dd HH:mm:ss")
                        .dateType(DateType.TIME_PACK).enableSwagger());

        fastAutoGenerator.packageConfig(c -> c.parent("com.test.ft.common.entity").entity("entity").mapper("mapper").service("service").serviceImpl("impl").controller("controller").xml("Mapper.xml")
                .pathInfo(Collections.singletonMap(OutputFile.xml, "F:\\xx\\ft-server" + "\\src\\main/resources/mapper"))// 设置XML资源文件的目录
        );
        fastAutoGenerator.strategyConfig(s ->
                s.enableCapitalMode().enableSkipView().disableSqlFilter().addInclude("company_t", "person_t").
                        addTableSuffix("_t") // 添加过滤表的后缀
                         .addTablePrefix("test_") // 添加过滤表的前缀
        );

        fastAutoGenerator.strategyConfig(s -> s.entityBuilder(). // 实体类设置
                enableLombok(). // 启用Lombok
                enableFileOverride(). // 启用文件覆盖
                enableTableFieldAnnotation(). // 生成实体时生成字段的注解,包括@TableId注解等-,可以不需要
                naming(NamingStrategy.underline_to_camel).  // 数据库表和字段映射到实体的命名策略,为下划线转驼峰.
                columnNaming(NamingStrategy.underline_to_camel).
                idType(IdType.AUTO)   // 全局主键类型为AUTO(自增)
                .superClass(BaseEntity.class).
                disableSerialVersionUID().enableChainModel().formatFileName("%sEntity"));
        // controller控制器设置
        fastAutoGenerator.strategyConfig(c -> c.controllerBuilder().
                enableRestStyle(). // 开启生成@RestController控制器
                        enableHyphenStyle().formatFileName("%sController")); // 开启驼峰转连字符 localhost:8080/hello_id_2
        // Service设置
        fastAutoGenerator.strategyConfig(s -> s.serviceBuilder().formatServiceFileName("%sService").formatServiceImplFileName("%sServiceImpl"));

        // Mapper设置
        fastAutoGenerator.strategyConfig(m -> m.mapperBuilder().formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").
                superClass(RootMapper.class). // 设置自定义继承的Mapper
                enableBaseResultMap(). // xml里面的基本<resultMap>
                enableBaseColumnList()); // xml里面的基础列
        // 模板引擎
        fastAutoGenerator.templateEngine(new FreemarkerTemplateEngine());
        fastAutoGenerator.execute();
    }

}

总结

以上就是MyBatis plus对MySQL实现批量新增,批量修改的内容,才疏学浅,如有错误,希望多多的指正。需要注意的是:**该方法只对MySQL测试过,其他的数据库支持度不一致,不一定适用
在这里插入图片描述

关注点:

在新增后,Mybatis Plus会自动返回新增的id。但是使用该方法批量新增后,只会返回新增的最后一条数据的id,如果需要在批量新增后返回所有的id,则需要使用IService里自带的saveBatch()方法

MyBatis - Plus 实现批量新增有以下几种常见方法: #### 1. 使用 `saveBatch` 方法 `saveBatch` 是 MyBatis - Plus 提供的一个 Service 层的方法,用于批量保存数据。该方法会将传入的实体列表批量插入到数据库中。 示例代码如下: ```java import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService extends ServiceImpl<UserMapper, User> { public boolean batchInsertUsers(List<User> userList) { return saveBatch(userList); } } ``` 在上述代码中,`UserMapper` 是 MyBatis - Plus 的 Mapper 接口,`User` 是实体类。调用 `saveBatch` 方法即可实现批量新增。 #### 2. 使用 `insertBatchSomeColumn` 方法 可以使用 `insertBatchSomeColumn` 方法来实现批量新增指定字段的操作。需要注意方法名必须为 `insertBatchSomeColumn`,要和 `InsertBatchSomeColumn` 内部定义好的方法名保持一致 [^1][^4]。 示例代码如下: ```java @Transactional(rollbackFor = Exception.class) @Override public void gradJiSiLuData() throws Exception { JslDataInfo instance = JslDataInfo.getInstance(); List<Kzz> list = instance.listKzz(true); if (ObjectUtil.isNotEmpty(list)) { kzzDao.truncateTable(); // 自定义方法插入,执行分片插入 List<List<Kzz>> partition = ListUtil.partition(list, 100); for (List<Kzz> kzzList : partition) { kzzDao.insertBatchSomeColumn(kzzList); } } } ``` #### 3. 自定义批量插入方法 可以自定义批量插入的 Mapper 方法。定义一个通用的 Mapper 接口,继承 `BaseMapper`,并添加自定义批量插入方法 [^3]。 示例代码如下: ```java /** * 公共 mapper * 要实现批量新增/修改 继承此类 * * @param <T> */ public interface CommonMapper<T> extends BaseMapper<T> { /** * 自定义批量插入 * 如果要自动填充,@Param(xx) xx 参数名必须是 list/collection/array 3 个的其中之一 */ int insertBatch(@Param("list") List<T> list); } ``` ### 总结 MyBatis - Plus 提供了多种实现批量新增的方法,可以根据具体需求选择合适的方法。`saveBatch` 方法使用简单,适用于一般的批量插入场景;`insertBatchSomeColumn` 方法可以指定插入的字段;自定义批量插入方法则更加灵活,可以根据业务需求进行定制。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值