您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。