mybatis-plus 批量插入/新增

发布时间 2023-06-01 10:10:50作者: shirleyLee

建表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for rewrite_sql
-- ----------------------------
DROP TABLE IF EXISTS `rewrite_sql`;
CREATE TABLE `rewrite_sql`  (
  `id` int NOT NULL DEFAULT -1,
  `stu_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `age` int NULL DEFAULT -1,
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

搭建项目

server.port=1222

#数据库
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/mysql?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root

#nocas
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#mapper
mybatis.mapper-locations=classpath:mapper/*.xml

#日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

 

@Data
@TableName("rewrite_sql")
public class RewriteSqlDO implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String stuName;

    private Integer age;

    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private Date createTime;
}

 

 1、一条条插入

@Test
    void test() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 10000; i < 20000; i++) {        //测试1w条数据耗时
            RewriteSqlDO rewriteSqlDO = new RewriteSqlDO();
            rewriteSqlDO.setId(i);
            rewriteSqlDO.setAge(i);
            rewriteSqlDO.setStuName(String.valueOf(1));
            rewriteSqlMapper.insert(rewriteSqlDO);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskTimeMillis());
    }

 【注】

优点:
1.spring自带工具类,可直接使用且简单
2.性能消耗小,展示清晰,start和stop之间时间的误差更小

缺点:

一个stopWatch只能开启一个task,必须在stop之后才能开启新的,如果需要开启多个,则需要创建多个stopWatch实例

 结果耗时:

 

2、使用foreach

public interface RewriteSqlMapper extends BaseMapper<RewriteSqlDO> {            //注意:这里用的是BaseMapper
    int insertBatch(@Param("list") List<RewriteSqlDO> list);
}
    <insert id="insertBatch" parameterType="java.util.List">
        insert into rewrite_sql(id, stu_name, age, create_time) values
        <foreach collection="list" item="item" separator=",">
            (#{item.id}, #{item.stuName}, #{item.age}, #{item.createTime})
        </foreach>
    </insert>

 在配置文件数据库配置中加入:&rewriteBatchedStatements=true

@Test
void test1() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    List<RewriteSqlDO> rewriteSqlDOList = new ArrayList<RewriteSqlDO>();
    for (int i = 1; i < 10000; i++) {
        RewriteSqlDO rewriteSqlDO = new RewriteSqlDO();
        rewriteSqlDO.setId(i);
        rewriteSqlDO.setAge(i);
        rewriteSqlDO.setStuName(String.valueOf(i));
        rewriteSqlDOList.add(rewriteSqlDO);
    }
    rewriteSqlMapper.insertBatch(rewriteSqlDOList);
    stopWatch.stop();
    System.out.println(stopWatch.getLastTaskTimeMillis());
}

结果耗时:

 

 3、自定义插入/更新

简述就是,封装好的foreach方法,性能耗时与第二种接近

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

    /**
     * 自定义批量插入
     * 如果要自动填充,@Param(xx) xx参数名必须是 list/collection/array 3个的其中之一
     */
    int insertBatch(@Param("list") List<T> list);

    /**
     * 自定义批量更新,条件为主键
     * 如果要自动填充,@Param(xx) xx参数名必须是 list/collection/array 3个的其中之一
     */
    int updateBatch(@Param("list") List<T> list);
    
}
/**
 * 批量新增
 */
@Slf4j
public class InsertBatchMethod extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s</script>";
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareValuesSql(tableInfo);
        final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
        log.debug("sqlResult----->{}", sqlResult);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三个参数必须和RootMapper的自定义方法名一致
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), null, null);
    }
  
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        tableInfo.getFieldList().forEach(x -> {
            //新增时修改字段不填充
            if (!("update_time".equals(x.getColumn()))
                    &&!("update_user_id".equals(x.getColumn()))
                    &&!("update_user_name".equals(x.getColumn()))){
                fieldSql.append(x.getColumn()).append(",");
            }
        });
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }
  
    private String prepareValuesSql(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        tableInfo.getFieldList().forEach(x -> {
            if (!("updateTime".equals(x.getProperty()))
                    &&!("updateUserId".equals(x.getProperty()))
                    &&!("updateUserName".equals(x.getProperty()))){
                valueSql.append("#{item.").append(x.getProperty()).append("},");
            }
        });
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}
/**
 * 批量更新方法实现,条件为主键,选择性更新
 */
@Slf4j
public class UpdateBatchMethod extends AbstractMethod {

    @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);
        log.debug("sqlResult----->{}", sqlResult);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三个参数必须和RootMapper的自定义方法名一致
        return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);
    }
  
}

 

@Slf4j
public class MyInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
//        log.info("==============methodList===================");
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new InsertBatchMethod());
        methodList.add(new UpdateBatchMethod());
        return methodList;
    }

}
@Slf4j
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
//        log.info("===================mybatisPlusInterceptor====================");
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }


    @Bean
    public MyInjector myInjector(){
//        log.info("========================myInjector===================================");
        return new MyInjector();
    }

}

 

@Test
void test2() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    List<RewriteSqlDO> rewriteSqlDOList = new ArrayList<RewriteSqlDO>();
    for (int i = 100000; i < 200000; i++) {
        RewriteSqlDO rewriteSqlDO = new RewriteSqlDO();
        rewriteSqlDO.setId(i);
        rewriteSqlDO.setAge(i);
        rewriteSqlDO.setStuName(String.valueOf(i));
        rewriteSqlDOList.add(rewriteSqlDO);
    }
    rewriteSqlMapper.insertBatch(rewriteSqlDOList);
    stopWatch.stop();
    System.out.println(stopWatch.getLastTaskTimeMillis());
}

 【注意】

 在配置文件数据库配置中加入:&allowMultiQueries=true

 

 结果耗时: