Java + Spring Boot + Mybatis 实现批量插入

在 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>

性能优化建议

  1. MySQL 参数 :在 JDBC URL 中添加 rewriteBatchedStatements=true,启用真正的批处理(提升 5-10 倍性能)。

  2. 批次大小:每批处理 500-1000 条数据(避免 SQL 过长)。

  3. 事务控制 :确保在 Service 层使用 @Transactional。

  4. 连接池:使用 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>
相关推荐
哦虎!2 小时前
【JavaEE】网络初识
java
for_ever_love__2 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
我是人✓3 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
欣栀♡3 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室4 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项4 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog4 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼4 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠4 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
Cc.Y5 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python