引言
在数据库设计中,主键自增(如 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插入数据时获取生成主键的资料请关注代码网其它相关文章!
发表评论