mybatis-plus saveBatch批量保存失效

问题说明

        在使用IService.savebatch方法批量插入数据时,观察控制台打印的Sql发现并没有像预想的一样,而是以逐条方式进行插入,插1000条数据就得10s多,正常假如批量插入应该是一条语句:

insert table (field1, field2) values (val1, val2), (val3, val4), (val5, val6), ... ;

而我的是这样:

insert table (field1, field2) values (val1, val2);
insert table (field1, field2) values (val3, val4);
...

那肯定很慢

从源码入手:

1、ServiceImpl.java

    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.sqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }


    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
        return !CollectionUtils.isEmpty(list) && this.executeBatch((sqlSession) -> {
            int size = list.size();
            int i = 1;

            for(Iterator var6 = list.iterator(); var6.hasNext(); ++i) {
                E element = var6.next();
                consumer.accept(sqlSession, element);
                if (i % batchSize == 0 || i == size) {
                    sqlSession.flushStatements();
                }
            }

        });
    }

 

2、MybatisBatchExecutor.java

    public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        Statement stmt;
        if (sql.equals(this.currentSql) && ms.equals(this.currentStatement)) {
            int last = this.statementList.size() - 1;
            stmt = (Statement)this.statementList.get(last);
            this.applyTransactionTimeout(stmt);
            handler.parameterize(stmt);
            BatchResult batchResult = (BatchResult)this.batchResultList.get(last);
            batchResult.addParameterObject(parameterObject);
        } else {
            Connection connection = this.getConnection(ms.getStatementLog());
            stmt = handler.prepare(connection, this.transaction.getTimeout());
            if (stmt == null) {
                return 0;
            }

            handler.parameterize(stmt);
            this.currentSql = sql;
            this.currentStatement = ms;
            this.statementList.add(stmt);
            this.batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }

        handler.batch(stmt);
        return -2147482646;
    }

一顿Step Into后进入了这个doUpdate方法,看了一下,if体内的应该就是批量拼接sql的关键,走了几个循环发现我的代码都是从else体里走了,那他为什么不进if呢,看了下判断条件,每次进来。statement都是一个,那问题就出在sql.equals(currentSql) 上面,我比对了下第二个实体的sql和第一个实体的sql,很快就发现了问题,他们竟然不!一!样!。

原因是在拼接insert语句时,如果实体的某个属性值为空,那他将不参与拼接,所以如果你的数据null值比较多且比较随机的分布在各个属性上,那生成出来的sql就会不一样,也就没法走批处理逻辑了

实体属性为null时,会影响生成的插入sql,进而影响批量保存逻辑

解决方案

定位到了问题,那就也便于解决了,问题原因是生成插入sql时,对null值的处理策略造成的

  • 全局配置insertStrategy为IGNORED
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.ts.core.modules.*.entity,com.ts.dataexchange.modules.*.entity
  global-config:
    #数据库相关配置
    db-config:
      #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: AUTO
      logic-delete-value: -1
      logic-not-delete-value: 0
      #在 insert 的时候的字段验证策略
      insert-strategy: ignored
  • 为可能受影响的属性添加注解
 @TableField(insertStrategy = FieldStrategy.IGNORED)
 private String content;
  • 自己重写个批量保存方法,自己写xml拼接sql,简单粗暴(小心sql超出最大长度)
  <insert id="insertBatch" parameterType="java.util.List">
        insert into table_name (id,code,name,content) VALUES
        <foreach collection ="list" item="entity" index= "index" separator =",">
            (
            #{entity.id}, #{entity.code}, #{entity.name}, #{entity.content}
            )
        </foreach>
</insert>

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: mybatis-plus的savebatch用法是批量插入数据的方法,可以将多条数据一次性插入到数据库中。使用该方法需要先创建一个实体类,然后通过该实体类的List集合来传递多条数据,最后调用savebatch方法即可完成批量插入操作。在使用该方法时需要注意,插入的数据必须符合数据库表的字段类型和约束条件,否则会插入失败。同时,该方法也支持自动生成主键的功能,可以通过在实体类的主键字段上添加注解来实现。 ### 回答2: Mybatis-plus是一个基于Mybatis的增强工具库,能够简化开发中的Mybatis操作。其中有一个saveBatch方法就是用来批量添加数据的,本文将详细介绍saveBatch的用法。 saveBatch方法的定义如下: ```java boolean saveBatch(Collection<T> entityList, int batchSize) ``` 其中,entityList表示待添加的数据列表,batchSize表示每批次添加的数量。 使用saveBatch方法的步骤如下: 1.在mapper接口中定义相应的方法。mapper接口需要继承BaseMapper接口,并指定类型参数,如下: ```java public interface UserMapper extends BaseMapper<User> { } ``` 2.实现mapper接口中的定义方法。如下: ```java public class UserMapperImpl extends ServiceImpl<UserMapper, User> implements UserMapper { } ``` 3.使用saveBatch方法添加数据。如下: ```java List<User> userList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { User user = new User(); user.setName("Tom" + i); user.setAge(i); userList.add(user); } userMapper.saveBatch(userList, 1000); ``` 注:上述代码中的userMapper为UserMapper类型的对象。 以上是saveBatch方法的基本使用方法。需要注意的是,当批次量较大时,可以适当调大batchSize的值。同时,还需注意存在主键冲突的情况,需要在代码中进行处理。如果需要批量添加不同类型的数据,则需要在mapper接口中定义多个方法,并实现相应的实现类。 总之,Mybatis-plus的saveBatch方法可以极大地简化添加数据的操作,提高代码效率,也是业务开发中经常使用的一个方法。 ### 回答3: Mybatis-Plus是一个基于Mybatis的增强工具,可以轻松地操作数据库。其中,SaveBatchMybatis-Plus针对批量插入数据提供的一种方法。SaveBatch的使用方法如下: 1.定义实体类 首先,我们需要定义一个实体类,该实体类的属性与数据库表字段相对应。 例如,定义一个User实体类,在User实体类中定义需要保存的字段属性: ```java @Data public class User { private Long id; private String name; private Integer age; } ``` 2.定义Mapper接口 接着,我们需要定义一个Mapper接口,用于定义需要对数据库进行的操作,包括插入、查询、删除等操作。在Mapper接口中定义SaveBatch方法,用于批量插入数据。 例如,定义一个UserMapper接口,在UserMapper接口中定义SaveBatch方法: ```java public interface UserMapper extends BaseMapper<User> { void saveBatch(List<User> userList); } ``` 3.调用SaveBatch方法 最后,我们可以通过Mybatis-Plus提供的BaseMapper接口,调用SaveBatch方法,将需要插入的数据集合传递给SaveBatch方法即可。 例如,在业务代码中,需要批量插入userList数据集合,可以调用UserMapper接口的SaveBatch方法: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public void insertBatch(List<User> userList) { userMapper.saveBatch(userList); } } ``` 需要注意的是,在批量插入数据时,数据库表中的字段需要与实体类中的属性名称一一对应,否则会报错。另外,SaveBatch方法执行效率较高,相较于逐条插入数据,可大大提高数据插入的效率。 以上就是Mybatis-Plus SaveBatch的用法,希望对大家有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值