mybatis-plus批量插入优化
- 背景
- 优化
- 新的问题
- 分批插入
- springboot3整合mybaits-plus
背景
使用的mybatisplus的批量插入方法:saveBatch(),打印 sql 日志发现,底层还是一条条的 insert 语句,这显然是不行的
优化
之前就看到过网上都在说在jdbc的url路径上加上rewriteBatchedStatements=true 参数mysql底层才能开启真正的批量插入模式。但是我已经添加了
通过查阅相关文档后,发现mybatisPlus提供了sql注入器,我们可以自定义方法来满足业务的实际开发需求。
sql 注入器官网:https://baomidou.com/guides/sql-injector/
mybatis-plus -core 核心包提供了基本的增删查改注入器,在批量插入数据这里显然不够,所以可以看到在 mybaits-plus-extension 包下还额外提供了批量插入的可注入方法
- AlwaysUpdateSomeColumnById: 根据Id更新每一个字段,全量更新不忽略null字段,解决mybatis-plus中updateById默认会自动忽略实体中null值字段不去更新的问题;
- InsertBatchSomeColumn: 真实批量插入,通过单SQL的insert语句实现批量插入;
- Upsert: 更新or插入,根据唯一约束判断是执行更新还是删除,相当于提供insert on duplicate key update支持。
我们只需要把这个方法添加进我们的sql注入器即可。
config包新增如下两个配置
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
//更新时自动填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
@Configuration
public class MybatisPlusConfig {
@Bean
public MySqlInjector sqlInjector() {
return new MySqlInjector();
}
}
原先的 mapper 是这么写的
public interface UserMapper extends BaseMapper<User> {
}
我们新增了 InsertBatchSomeColumn 方法,需要重新定义一个 BaseMapper
public interface CommonMapper<T> extends BaseMapper<T> {
/**
* 真正的批量插入
* @param entityList
* @return
*/
int insertBatchSomeColumn(List<T> entityList);
}
public interface UserMapper extends CommonMapper<User> {
}
优化后的接口就对了,sql 显示确实是 批量插入的语句
新的问题
上面虽然实现了真正意义上的sql层面的批量插入。
但是,到这里并没有结束,mybatisPlus官方提供的 insertBatchSomeColumn 方法不支持分批插入,**也就是有多少直接全部一次性插入,这就可能会导致最后的 sql 拼接语句特别长,超出了mysql 的限制,**于是我们还要实现一个类似于saveBatch 分批的批量插入方法。
分批插入
模仿原来的saveBatch方法:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
@Transactional(rollbackFor = {Exception.class})
public boolean saveBatch(Collection<User> entityList, int batchSize) {
try {
int size = entityList.size();
int idxLimit = Math.min(batchSize, size);
int i = 1;
//保存单批提交的数据集合
List<User> oneBatchList = new ArrayList<>();
for (Iterator<User> it = entityList.iterator(); it.hasNext(); ++i) {
User element = it.next();
oneBatchList.add(element);
if (i == idxLimit) {
baseMapper.insertBatchSomeColumn(oneBatchList);
//每次提交后需要清空集合数据
oneBatchList.clear();
idxLimit = Math.min(idxLimit + batchSize, size);
}
}
} catch (Exception e) {
log.error("saveBatch fail", e);
return false;
}
return true;
}
}
从下面结果可以看到,最终的 sql 分成了两个批次,这样的话 sql 语句就不会太长
springboot3整合mybaits-plus
这里就简单粘贴一下pom文件,注意 用mybatis-plus-spring-boot3-starter 这个依赖,不是用 mybatis-plus-spring-boot-starter ,不然报错
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.springbootV3</groupId>
<artifactId>springbootV3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springbootV3</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring.datasource.url=jdbc:mysql://192.168.133.128:3306/wxpay?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath:/com/example/demo/**/*Mapper.xml