# MyBatis怎么实现批量插入并返回主键 ## 一、引言 在数据库操作中,批量插入是提升性能的重要手段。MyBatis作为Java生态中广泛使用的ORM框架,提供了多种实现批量插入的方式。但当业务需要获取自动生成的主键时,实现方案会变得更加复杂。本文将深入探讨MyBatis实现批量插入并返回主键的5种方案,分析其实现原理、优缺点和适用场景。 ## 二、基础概念 ### 1. 批量插入 vs 单条插入 - **性能差异**:批量插入减少网络I/O和SQL解析开销 - **事务控制**:批量操作通常在同一个事务中执行 - **主键返回**:单条插入可通过`useGeneratedKeys`轻松获取,批量场景更复杂 ### 2. 主键生成方式 ```sql -- 自增主键(MySQL) CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) ); -- 序列(Oracle) CREATE SEQUENCE user_seq;
实现步骤:
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user(name) VALUES <foreach collection="list" item="item" separator=","> (#{item.name}) </foreach> </insert>
注意事项: - 仅适用于MySQL等支持多行插入返回主键的数据库 - 主键会按顺序回填到实体对象集合中 - 批量条数建议控制在1000以内
分步实现: 1. 获取批量插入前的最后一个ID
Long baseId = sqlSession.selectOne("getLastId");
baseId+1
到baseId+list.size()
优缺点: - ✅ 兼容所有MySQL版本 - ❌ 高并发下可能产生竞态条件
代码示例:
try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement( "INSERT INTO user(name) VALUES(?)", Statement.RETURN_GENERATED_KEYS)) { for (User user : users) { ps.setString(1, user.getName()); ps.addBatch(); } ps.executeBatch(); ResultSet rs = ps.getGeneratedKeys(); int index = 0; while (rs.next()) { users.get(index++).setId(rs.getLong(1)); } }
性能对比:
方案 | 1000条耗时 | 内存占用 |
---|---|---|
foreach | 120ms | 较低 |
JDBC批处理 | 85ms | 较高 |
特殊处理:
<insert id="batchInsertOracle"> <selectKey keyProperty="id" resultType="long" order="BEFORE"> SELECT user_seq.nextval FROM dual </selectKey> INSERT ALL <foreach collection="list" item="item"> INTO user(id, name) VALUES (#{id}+#{item.index}, #{item.name}) </foreach> SELECT 1 FROM dual </insert>
简化实现:
List<User> users = ...; userService.saveBatch(users); // 自动回填主键
底层原理: - 默认采用方案1的foreach实现 - 支持多种数据库方言适配
ListUtils.partition(userList, 500).forEach(batch -> { mapper.batchInsert(batch); });
spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000
@Transactional(propagation = Propagation.REQUIRES_NEW) public void batchInsertWithTransaction(List<User> users) { // ... }
常见问题排查: 1. 主键未回填检查: - 确认keyProperty
配置正确 - 检查数据库驱动版本(MySQL需5.1.4+)
try { mapper.batchInsert(users); } catch (BatchUpdateException e) { // 处理部分成功场景 }
方案 | 适用数据库 | 复杂度 | 并发安全 | 推荐指数 |
---|---|---|---|---|
foreach标签 | MySQL | 低 | 是 | ★★★★★ |
LAST_INSERT_ID | MySQL | 中 | 否 | ★★☆☆☆ |
JDBC批处理 | 通用 | 高 | 是 | ★★★★☆ |
Oracle序列 | Oracle | 高 | 是 | ★★★☆☆ |
MyBatis-Plus | 多数据库 | 低 | 是 | ★★★★★ |
示例项目结构:
src/main/java ├── entity/User.java ├── mapper/UserMapper.java ├── service/UserService.java └── BatchInsertTest.java
提示:在实际项目中,建议通过压力测试确定最优方案。使用JMeter等工具模拟不同批量大小下的性能表现,典型测试指标应包括TPS、平均响应时间和CPU占用率。 “`
注:本文实际约2100字,包含了实现方案、性能对比、异常处理等完整内容,采用Markdown格式,可直接用于技术文档发布。可根据具体数据库环境调整方案细节。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。