在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach>
标签和批处理模式(ExecutorType.BATCH)。
方法一:使用 XML 的 <foreach>
标签(推荐)
这是最高效的方式,通过单条 SQL 语句一次性插入多条数据。
1. Mapper 接口
java
复制
下载
@Mapper public interface UserMapper { // 批量插入方法 int batchInsert(@Param("users") List<User> users); }
2. XML 映射文件 (UserMapper.xml
)
xml
复制
下载
运行
<insert id="batchInsert"> INSERT INTO user (name, email, age) VALUES <foreach collection="users" item="user" separator=","> (#{user.name}, #{user.email}, #{user.age}) </foreach> </insert>
3. Service 层实现
java
复制
下载
@Service @RequiredArgsConstructor // Lombok 注解 public class UserService { private final UserMapper userMapper; @Transactional public void batchInsertUsers(List<User> users) { if (users != null && !users.isEmpty()) { userMapper.batchInsert(users); } } }
4. 实体类
java
复制
下载
@Data // Lombok 注解 public class User { private Long id; private String name; private String email; private Integer age; }
方法二:使用 MyBatis 批处理模式
适合需要精确控制事务的场景,但性能略低于 <foreach>
方式。
1. Mapper 接口
java
复制
下载
@Mapper public interface UserMapper { // 单条插入方法 void insert(User user); }
2. XML 映射文件
xml
复制
下载
运行
<insert id="insert"> INSERT INTO user (name, email, age) VALUES (#{name}, #{email}, #{age}) </insert>
3. Service 层实现
java
复制
下载
@Service @RequiredArgsConstructor public class UserService { private final SqlSessionTemplate sqlSessionTemplate; @Transactional public void batchInsert(List<User> users) { // 获取批处理模式的 SqlSession SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory() .openSession(ExecutorType.BATCH, false); // 关闭自动提交 try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); for (User user : users) { mapper.insert(user); } sqlSession.commit(); // 手动提交 } catch (Exception e) { sqlSession.rollback(); throw e; } finally { sqlSession.close(); } } }
关键配置
1. application.yml
配置
yaml
复制
下载
spring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&rewriteBatchedStatements=true # MySQL 批处理优化 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml # XML 文件位置
2. 添加依赖 (pom.xml
)
xml
复制
下载
运行
<dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis + Spring Boot --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
性能优化建议
MySQL 参数:在 JDBC URL 中添加
rewriteBatchedStatements=true
,启用真正的批处理(提升 5-10 倍性能)。批次大小:每批处理 500-1000 条数据(避免 SQL 过长)。
事务控制:确保在 Service 层使用
@Transactional
。连接池:使用 HikariCP 等高性能连接池(Spring Boot 默认集成)。
两种方法对比
方法 | 优点 | 缺点 |
---|---|---|
<foreach> 标签 |
单次数据库交互,性能最高 | 超大数据可能使 SQL 过长 |
ExecutorType.BATCH | 灵活控制事务,内存占用低 | 需要手动提交,性能略低 |
根据数据量选择合适方案:
数据量 < 1000:推荐
<foreach>
数据量 > 10000:推荐分批次 +
<foreach>
(每批 500 条)
项目实例:
BranchWarehouseApplyServiceImpl.java
// 生成分仓物资申领(试剂耗材)
@Override
public void generateDetailForReagent(List<ReagentOptionVO> reagentOptionList) {
// 获取用户名(账号)
String userName = PublicUtils.getUserName();
// 通过用户名(账号),获取其所属部门
Department department = departmentMapper.selectByUserName(userName);
List<BranchWarehouseApplyDetail> applyDetailList = new ArrayList<>();
reagentOptionList.forEach(item -> {
BranchWarehouseApplyDetail applyDetail = new BranchWarehouseApplyDetail();
applyDetail.setWarehouseId(1);
applyDetail.setMaterialId(item.getId());
applyDetail.setApplyTime(LocalDateTime.now());
applyDetail.setApplyDept(department.getDeptId());
applyDetail.setApplyOperator(userName);
applyDetail.setAmount(item.getApplyAmount());
applyDetail.setStage(0);
applyDetailList.add(applyDetail);
});
// 集合不为空
if (!applyDetailList.isEmpty()) {
// 批处理,拆分数据,满50条,执行批量插入
List<List<BranchWarehouseApplyDetail>> lists = (List<List<BranchWarehouseApplyDetail>>)PublicUtils.splitList(applyDetailList, 50);
for (List<BranchWarehouseApplyDetail> list: lists) {
branchWarehouseApplyMapper.insertDetail(list);
}
}
}
BranchWarehouseApplyMapper.java
@Mapper
public interface BranchWarehouseApplyMapper {
// 批量增加分仓物资申领
void insertDetail(List<BranchWarehouseApplyDetail> applyDetailList);
}
BranchWarehouseApplyMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.weiyu.mapper.BranchWarehouseApplyMapper">
<!-- 分仓物资申领 mssql -->
<!-- 批量增加分仓物资申领,使用单条INSERT语句配合VALUES列表更高效 -->
<insert id="insertDetail">
insert into BranchWarehouseApplyDetail (
warehouse_id, material_id, apply_time, apply_dept, apply_operator,
amount, purpose, stage, remark
) values
<!-- 非空列表,并且有记录 -->
<if test="applyDetailList != null and applyDetailList.size() > 0">
<foreach collection="applyDetailList" item="item" separator=",">
(
#{item.warehouseId}, #{item.materialId}, #{item.applyTime}, #{item.applyDept}, #{item.applyOperator},
#{item.amount}, #{item.purpose}, #{item.stage}, #{item.remark}
)
</foreach>
</if>
<!-- 空集合安全处理:当传入空列表时,WHERE 1=0确保不插入实际数据,同时避免数据库报错 -->
<if test="applyDetailList == null or applyDetailList.size() == 0">
(null, null, null, null, null, null, null, null, null) where 1 = 0
</if>
</insert>
</mapper>