在 MyBatis 中使用 useGeneratedKeys="true" 获取新插入记录的自增 ID 值,可通过以下步骤实现:
1. 配置 Mapper XML
在插入语句的 <insert> 标签中设置:
xml
复制
下载
运行
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name, email)VALUES (#{name}, #{email})
</insert>
-
useGeneratedKeys="true":启用数据库生成的主键(如自增 ID)。 -
keyProperty="id":将生成的主键值赋给参数对象(如User对象)的id属性。
2. 定义实体类
确保实体类有对应的属性(与 keyProperty 一致):
java
复制
下载
public class User {private Long id; // 属性名必须与 keyProperty 匹配private String name;private String email;// Getter & Setter
}
3. 调用 Mapper 方法
插入后,自动填充的 ID 会直接赋给传入的实体对象:
java
复制
下载
User newUser = new User();
newUser.setName("John");
newUser.setEmail("john@example.com");// 执行插入(返回的是影响行数,非 ID)
int rows = userMapper.insertUser(newUser); // 插入后,自增 ID 已注入 newUser 的 id 属性
Long newId = newUser.getId(); // ✅ 直接获取
System.out.println("新记录的 ID:" + newId);
关键点说明
-
keyProperty必须匹配实体属性名
如实体属性为userId,则需配置keyProperty="userId"。 -
支持批量插入(MyBatis 3.3.1+)
配置keyProperty为集合元素的属性:xml
复制
下载
运行
<insert id="insertUsers" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name) VALUES <foreach collection="list" item="user" separator=",">(#{user.name})</foreach> </insert>调用后,每个对象的
id都会被赋值:java
复制
下载
List<User> users = Arrays.asList(new User("A"), new User("B")); userMapper.insertUsers(users); users.forEach(u -> System.out.println(u.getId())); // 输出所有新 ID -
数据库兼容性
-
MySQL/SQL Server:直接支持
useGeneratedKeys。 -
Oracle:需用
<selectKey>配合序列(非本方案范畴)。
-
常见问题
-
获取值为
null?
检查keyProperty是否与实体属性名一致,或数据库是否真的生成了自增 ID。 -
批量插入无效?
确保 MyBatis 版本 ≥ 3.3.1,且keyProperty指向集合元素的属性(如id)。
通过此方案,插入后无需额外查询,ID 值直接注入对象属性,高效简洁。
在 MyBatis 中,keyProperty 可以指定为参数对象中的任意属性路径(包括嵌套对象或 Map 的键值),不一定必须是主实体对象的属性。以下是几种灵活用法:
1. 指定参数对象的嵌套属性
若参数是一个包含 ID 容器的复合对象:
java
复制
下载
public class InsertParam {private User user;private Long generatedId; // 专门接收 ID 的属性// Getter & Setter
}
Mapper XML 配置:
xml
复制
下载
运行
<insert id="insertUser" parameterType="InsertParam" useGeneratedKeys="true" keyProperty="generatedId">INSERT INTO user(name) VALUES (#{user.name})
</insert>
插入后获取:
java
复制
下载
InsertParam param = new InsertParam();
param.setUser(new User("Alice"));
userMapper.insertUser(param);Long newId = param.getGeneratedId(); // ✅ 从专用属性获取
2. 使用 Map 参数接收
直接通过 Map 传递参数并接收 ID:
xml
复制
下载
运行
<insert id="insertUser" parameterType="map"useGeneratedKeys="true" keyProperty="resultId">INSERT INTO user(name) VALUES (#{name})
</insert>
Java 调用:
java
复制
下载
Map<String, Object> params = new HashMap<>();
params.put("name", "Bob");userMapper.insertUser(params);Long newId = (Long) params.get("resultId"); // ✅ 从 Map 获取
3. 多参数场景(结合 @Param)
当方法有多个参数时,用 @Param 指定命名空间:
java
复制
下载
int insertUser(@Param("user") User user,@Param("idHolder") Map<String, Long> idHolder // 专门存 ID 的 Map
);
Mapper XML 配置:
xml
复制
下载
运行
<insert id="insertUser" useGeneratedKeys="true" keyProperty="idHolder.id">INSERT INTO user(name) VALUES (#{user.name})
</insert>
插入后获取:
java
复制
下载
Map<String, Long> idHolder = new HashMap<>();
User user = new User("Charlie");userMapper.insertUser(user, idHolder);Long newId = idHolder.get("id"); // ✅ 从独立 Map 获取
关键规则
-
路径匹配原则
keyProperty的值是 OGNL 表达式,需严格匹配参数对象的属性路径:-
单参数:直接写属性名(如
generatedId) -
多参数:用
@Param别名前缀(如idHolder.id)
-
-
批量插入场景
批量操作时keyProperty需指向集合元素的属性(不支持外部容器):xml
复制
下载
运行
<!-- 正确:ID 注入到每个元素的 id 属性 --> <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name) VALUES <foreach item="item" collection="list" separator=",">(#{item.name})</foreach> </insert> -
常见问题
-
属性未注入? 检查
keyProperty路径是否存在 setter 方法 -
返回
null? 确保数据库自增 ID 已生效(如 MySQL 的AUTO_INCREMENT)
-
使用建议
-
专用 ID 容器:推荐用独立
Map或 DTO 属性接收 ID,避免污染实体对象 -
明确路径:多参数时用
@Param定义清晰命名空间(如keyProperty="holder.id") -
事务安全:插入后立即从参数对象中获取 ID,确保在同一事务内
当 Mapper 方法有多个参数时,MyBatis 无法确定将生成的自增 ID 注入到哪个参数中。以下是详细解决方案:
错误原因分析
复制
下载
Cause: org.apache.ibatis.executor.ExecutorException: Could not determine which parameter to assign generated keys to. Note that when there are multiple parameters, 'keyProperty' must include the parameter name
-
当 Mapper 方法有多个参数时,必须使用 参数名前缀 明确指定注入位置
-
您当前配置的
keyProperty="generatedId"缺少参数名前缀 -
MyBatis 找到的可用参数:
[generatedId, ckDetailId, param1, param2]
解决方案:添加参数名前缀
方法 1:使用 @Param 注解明确命名参数
java
复制
下载
// Mapper 接口方法
int insertRecord(@Param("data") Map<String, Object> data, // 数据参数@Param("idHolder") Map<String, Long> idHolder // 专门接收ID的参数
);
xml
复制
下载
运行
<!-- Mapper XML 配置 -->
<insert id="insertRecord" useGeneratedKeys="true" keyProperty="idHolder.generatedId"> <!-- 添加参数名前缀 -->INSERT INTO your_table(...) VALUES (#{data.field1}, #{data.field2})
</insert>
方法 2:使用 MyBatis 默认参数名(不推荐)
xml
复制
下载
运行
<insert id="insertRecord" useGeneratedKeys="true" keyProperty="param2.generatedId"> <!-- 使用paramN格式 -->... </insert>
参数索引从1开始:
param1=第一个参数,param2=第二个参数
完整示例代码
1. Mapper 接口
java
复制
下载
public interface YourMapper {int insertRecord(@Param("detail") YourEntity entity, // 实体参数@Param("idHolder") Map<String, Long> holder // ID容器参数);
}
2. Mapper XML
xml
复制
下载
运行
<insert id="insertRecord" useGeneratedKeys="true"keyProperty="idHolder.generatedId"> <!-- 关键:带前缀的keyProperty -->INSERT INTO your_table(name, ck_detail_id)VALUES (#{detail.name}, #{detail.ckDetailId})
</insert>
3. 调用代码
java
复制
下载
// 准备参数
YourEntity entity = new YourEntity("test", 1001);
Map<String, Long> idHolder = new HashMap<>(); // 专门接收ID// 执行插入
mapper.insertRecord(entity, idHolder); // 获取自增ID (注意键名与XML中一致)
Long newId = idHolder.get("generatedId");
System.out.println("Generated ID: " + newId);
关键注意事项
-
前缀必须匹配
@Param值
keyProperty="idHolder.generatedId"对应:-
idHolder→@Param("idHolder") -
generatedId→ Map中的键名
-
-
Map 接收 vs 对象属性
xml
复制
下载
运行
<!-- 如果使用对象属性接收 --> keyProperty="detail.id" <!-- 注入到实体对象的id属性 --><!-- 如果使用Map接收 --> keyProperty="idHolder.generatedId" <!-- 注入到Map的generatedId键 -->
-
批量插入特殊处理
批量操作时仍需注入到集合元素内:xml
复制
下载
运行
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">INSERT ... <!-- 无法注入到外部参数 --> </insert>
常见错误排查
| 错误现象 | 解决方案 |
|---|---|
返回null | 检查数据库自增列是否生效 |
报Invalid bound statement | 检查keyProperty前缀是否匹配@Param值 |
| 批量插入ID未注入 | 确保keyProperty指向集合元素的属性 |
| Oracle数据库不生效 | 需使用<selectKey>替代 |
建议:对多参数场景始终使用
@Param+ 前缀的keyProperty,避免依赖paramN隐式命名
