在实际开发中,批量插入数据是一个常见需求,尤其当数据量较大时,直接使用循环插入效率低下,而 MyBatis-Plus 提供了强大的批量操作支持。本文将详细讲解如何通过配置和代码实现 batchSave
接口的分页批量插入功能,优化性能并避免内存溢出。
MyBatis-Plus 的 BaseMapper
默认提供了 insert
方法,但它只支持单条插入。对于批量插入,通常需要借助 MyBatis 的批量操作能力,而 MP 也支持通过自定义方式实现高效的批量插入。分页批量插入的核心思想是将大数据量分批处理,既保证性能,又避免一次性加载过多数据导致内存压力。
确保项目中已引入 MyBatis-Plus 依赖(以 Spring Boot 为例):
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3version>
dependency>
在 application.yml
中配置数据源和 MyBatis-Plus:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
MyBatis-Plus 本身没有直接提供 batchSave
接口,我们需要通过自定义 Mapper 和 XML 文件实现分页批量插入功能。
假设有一个 User
实体类:
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
// getters and setters
}
定义一个 UserMapper
接口,添加批量插入方法:
public interface UserMapper extends BaseMapper<User> {
/**
* 批量插入用户
* @param userList 用户列表
* @return 插入成功的记录数
*/
int batchInsert(@Param("list") List<User> userList);
}
在 src/main/resources/mapper/UserMapper.xml
中实现 batchInsert
的 SQL:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO t_user (name, age)
VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age})
foreach>
insert>
mapper>
这里使用了 MyBatis 的
标签,动态生成批量插入的 SQL。
在 Service 层实现分页批量插入:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 分页批量插入用户
* @param userList 待插入的用户列表
* @param batchSize 每批次插入的数量
*/
public void batchSaveUsers(List<User> userList, int batchSize) {
if (userList == null || userList.isEmpty()) {
return;
}
// 计算总批次
int totalSize = userList.size();
int batchCount = (totalSize + batchSize - 1) / batchSize;
for (int i = 0; i < batchCount; i++) {
// 计算每批的起始和结束索引
int start = i * batchSize;
int end = Math.min(start + batchSize, totalSize);
// 分批截取数据
List<User> subList = userList.subList(start, end);
// 调用批量插入
userMapper.batchInsert(subList);
}
}
}
在 Controller 或测试类中调用:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/batchInsert")
public String batchInsert() {
// 模拟生成 1000 条数据
List<User> userList = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
User user = new User();
user.setName("用户" + i);
user.setAge(20 + (i % 10));
userList.add(user);
}
// 每批次插入 100 条
userService.batchSaveUsers(userList, 100);
return "批量插入完成";
}
}
batchSize
不宜过大或过小,通常建议设置为 100-1000,根据数据库性能和数据量调整。rewriteBatchedStatements=true
,可以将批量插入重写为单条 SQL,提升性能:spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
对于大批量插入,建议添加事务管理,确保数据一致性:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional(rollbackOn = Exception.class)
public void batchSaveUsers(List<User> userList, int batchSize) {
// 同上逻辑
}
}
在实际场景中,可能需要捕获异常并记录失败的数据:
public void batchSaveUsers(List<User> userList, int batchSize) {
for (int i = 0; i < batchCount; i++) {
int start = i * batchSize;
int end = Math.min(start + batchSize, userList.size());
List<User> subList = userList.subList(start, end);
try {
userMapper.batchInsert(subList);
} catch (Exception e) {
System.err.println("第 " + (i + 1) + " 批插入失败: " + e.getMessage());
}
}
}
运行代码后,可以检查数据库中 t_user
表的数据是否正确插入。如果日志开启,可以看到类似以下的 SQL:
INSERT INTO t_user (name, age) VALUES ('用户1', 20), ('用户2', 21), ...
通过调整 batchSize
和观察执行时间,可以找到最优配置。
通过自定义 batchInsert
接口并结合分页逻辑,我们实现了 MyBatis-Plus 的高效批量插入功能。分页批量插入不仅能处理大数据量,还能有效避免内存溢出问题。结合事务管理和性能优化参数,可以进一步提升其在生产环境中的表现。
希望这篇博文能为你提供实用的参考!如果有其他疑问,欢迎留言讨论。