MyBatis插入数据时获取生成主键的两种方法
作者:步行cgn
引言
在数据库设计中,主键自增(如 MySQL 的 AUTO_INCREMENT)是最常见的场景。当我们在代码中插入一条数据时,往往需要立即获取这条数据在数据库中生成的唯一主键 ID(例如用于后续关联操作或返回给前端)。
MyBatis 提供了两种强大的机制来实现这一点:useGeneratedKeys + keyProperty(推荐)和 <selectKey> 标签。下面我将从用法、底层原理到常见陷阱为你全面拆解。
一、方法一:useGeneratedKeys+keyProperty(最推荐)
这是 MyBatis 最简洁、最直接的方式,利用 JDBC 底层的 Statement.getGeneratedKeys() 方法实现。它适用于支持自动生成主键的数据库(MySQL、SQL Server、PostgreSQL 等)。
1. XML 映射文件配置
在 <insert> 标签中开启两个属性:
useGeneratedKeys="true":告诉 MyBatis 使用 JDBC 的getGeneratedKeys方法获取主键。keyProperty="id":指定将获取到的主键值赋值给实体类的哪个属性。
<insert id="insertCar" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_car (car_num, brand, guide_price, produce_time, car_type)
VALUES (#{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
</insert>注意:这里无需在 SQL 中写 id 字段,数据库会自动生成。
2. Java 注解配置(对应接口)
如果使用注解开发,只需加上 @Options 注解:
@Insert("INSERT INTO t_car (car_num, brand) VALUES (#{carNum}, #{brand})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertCar(Car car);
3. 底层工作原理
- MyBatis 执行
PreparedStatement时,会调用Connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)。 - 执行 SQL 后,JDBC 驱动通过
ResultSet generatedKeys = statement.getGeneratedKeys()获取生成的主键结果集。 - MyBatis 自动解析结果集,并通过反射将值注入到
car对象的id属性中。
4. Java 代码端使用
Car car = new Car();
car.setCarNum("1003");
car.setBrand("宝马");
// 执行插入
int rows = carMapper.insertCar(car);
// 此时 car 对象的 id 已经被自动赋值为数据库生成的主键!
System.out.println("插入后生成的ID: " + car.getId()); // 输出: 插入后生成的ID: 1
二、方法二:<selectKey>标签(更灵活)
当需要获取的值不是自增主键(如 UUID、Oracle 的序列 Sequence)时,或者在插入前就需要知道主键值,可以使用 <selectKey> 标签。
1. 应用场景:插入前获取 UUID(order="BEFORE")
<insert id="insertUser">
<!-- 先执行查询,生成 UUID 并赋值给 user.id,再执行插入 -->
<selectKey keyProperty="id" resultType="String" order="BEFORE">
SELECT UUID() <!-- MySQL 生成 UUID 函数 -->
</selectKey>
INSERT INTO t_user (id, username) VALUES (#{id}, #{username})
</insert>2. 应用场景:Oracle 序列自增(order="AFTER")
Oracle 没有自增主键,通常使用序列(Sequence)。需要在插入后获取序列值,使用 order="AFTER"。
<insert id="insertEmployee">
<selectKey keyProperty="empId" resultType="int" order="AFTER">
SELECT SEQ_EMPLOYEE.CURRVAL FROM DUAL
</selectKey>
INSERT INTO t_employee (name, age) VALUES (#{name}, #{age})
</insert>注意:order 属性控制查询语句的执行时机。
BEFORE:先执行selectKey查询,将结果设置给对象,再执行INSERT。AFTER:先执行INSERT,再执行selectKey查询(用于查询当前序列值)。
三、特殊情况处理:批量插入时获取主键
当进行批量插入时,依然可以使用 useGeneratedKeys,MyBatis 会为列表中的每一个对象都注入生成的主键。
XML 配置
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_car (car_num, brand) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.carNum}, #{item.brand})
</foreach>
</insert>Java 测试
List<Car> carList = new ArrayList<>();
carList.add(new Car("A001", "宝马"));
carList.add(new Car("A002", "奔驰"));
carMapper.insertBatch(carList);
// 遍历列表,每个 car 对象都已填充了对应的自增 ID
carList.forEach(car -> System.out.println(car.getId()));
// 输出: 101, 102 (取决于当前自增值)
四、易错点与避坑指南(必看)
忘记写 keyProperty
- 如果只写了
useGeneratedKeys="true"而忘记指定keyProperty="id",MyBatis 虽然获取了主键,但不知道赋给谁,对象里的id依然为null。
keyProperty 写错属性名
- 属性名必须与实体类中的字段名严格一致(如实体类是
carNum,配置keyProperty="carNum"就是错的,应该是id)。
XML 中的 parameterType 与 keyProperty 的关系
keyProperty指向的是parameterType指定类的属性。如果是多参数或 Map,需要确保该 Map 中包含对应的 Key。
数据库驱动版本兼容性
- 某些老旧的 MySQL 驱动版本(如 5.1.6 之前)对
getGeneratedKeys支持不完善,建议升级到较新版本。
<selectKey> 与 resultType
- 必须在
<selectKey>中明确指定resultType,否则 MyBatis 无法确定返回值的类型,可能导致类型转换异常。
五、总结对比
| 特征 | useGeneratedKeys + keyProperty | <selectKey> 标签 |
|---|---|---|
| 适用数据库 | MySQL、SQL Server、PostgreSQL(自增/Identity) | 所有数据库(包括 Oracle、UUID 等) |
| 配置复杂度 | 极简(仅两个属性) | 稍复杂(需配置查询语句和顺序) |
| 获取时机 | 执行 INSERT 之后 | 可自定义 BEFORE 或 AFTER |
| 底层依赖 | JDBC 的 getGeneratedKeys() 方法 | 执行额外的 SQL 查询语句 |
| 推荐指数 | ⭐⭐⭐⭐⭐(首选) | ⭐⭐⭐⭐(仅用于 Oracle、UUID 等特殊场景) |
六、最佳实践总结
- 使用
useGeneratedKeys是最优雅的方式,它能确保你的代码与数据库驱动紧密结合,且仅执行一次 SQL,性能最优。 - 在你的
CarMapper.xml中,请务必将<insert>修改为:
<insert id="insertCar" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_car (car_num, brand, guide_price, produce_time, car_type)
VALUES (#{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
</insert>- 运行代码后,直接调用
car.getId()即可拿到主键,彻底告别SELECT LAST_INSERT_ID()的繁琐操作!
以上就是MyBatis插入数据时获取生成主键的两种方法的详细内容,更多关于MyBatis插入数据时获取生成主键的资料请关注脚本之家其它相关文章!
