当前位置: 代码网 > it编程>编程语言>Java > MyBatis主键回填的两种实现方式

MyBatis主键回填的两种实现方式

2026年04月08日 Java 我要评论
购物下单后,订单号是怎么自动生成的?插入数据后如何直接获取自增主键?本文将带你深入理解 mybatis 中的主键回填机制,并通过流程图和代码示例彻底掌握它。一、业务场景:下单后如何返回订单编号?想象一

购物下单后,订单号是怎么自动生成的?插入数据后如何直接获取自增主键?本文将带你深入理解 mybatis 中的主键回填机制,并通过流程图和代码示例彻底掌握它。

一、业务场景:下单后如何返回订单编号?

想象一个典型的购物交易流程:

  1. 用户点击“立即购买”,填写收货信息,提交订单。
  2. 后端服务将订单数据(用户id、商品id、金额、状态等)插入到数据库的 orders 表中。
  3. 插入成功后,系统需要立即返回一个订单编号给前端,用于后续支付、查询等操作。

这个订单编号通常是数据库表的主键,比如自增的 id 字段。但在执行插入 sql 时,我们并不知道这个 id 是多少 —— 它是数据库自动生成的。

如果采用“先插入,再查询”的方式:

-- 第一步:插入订单(id 自动生成)
insert into orders(user_id, amount, status) values(1001, 299.00, 0);
-- 第二步:根据唯一业务字段查询刚插入的订单
select id from orders where user_id = 1001 and create_time = ...;

这种做法不仅繁琐,而且在高并发下很容易出错(多个用户同时下单,条件可能不唯一)。于是,主键回填技术应运而生。

二、什么是主键回填?

主键回填(key fillback / key return)是指在执行数据库插入操作后,自动将生成的主键值填充到传入的 java 对象中。这样,你无需再次查询,就能直接通过对象的 getid() 方法拿到主键。

简单来说:插入时主键为 null,插入后 mybatis 帮你把主键值“填回去”

效果演示

order order = new order();
order.setuserid(1001);
order.setamount(299.00);
order.setstatus(0);
// 此时 order.getid() == null
ordermapper.insertorder(order);
// 插入后,mybatis 自动将生成的主键赋值给 order 对象
system.out.println(order.getid()); // 输出 12345

三、主键回填的两种实现方式

mybatis 主要提供两种方式实现主键回填,分别适用于不同的数据库和场景。

方式一:usegeneratedkeys+keyproperty(推荐,简单高效)

适用数据库:支持自动生成主键的数据库,如 mysql、sql server(自增列)、postgresql(serial 类型)等。

原理:mybatis 利用 jdbc 的 statement.getgeneratedkeys() 方法获取数据库自动生成的主键。

配置示例

<insert id="insertorder" usegeneratedkeys="true" keyproperty="id">
    insert into orders(user_id, amount, status)
    values (#{userid}, #{amount}, #{status})
</insert>
  • usegeneratedkeys="true":开启主键回填功能。
  • keyproperty="id":指定将生成的主键值赋给 java 对象的哪个属性(对应 order 类中的 id 字段)。

java 接口方法

public interface ordermapper {
    int insertorder(order order);
}

使用注意

  • 只能用于单条插入(批量插入时需特殊处理,不同数据库支持情况不一)。
  • 数据库表必须定义自增列或类似机制。

方式二:<selectkey>标签(灵活,支持多种数据库)

适用场景

  • 数据库没有自增列(如 oracle 使用序列)。
  • 需要在插入前生成主键(比如使用 uuid)。
  • 需要执行自定义查询来获取主键值(如 mysql 的 last_insert_id())。

原理:通过子查询执行一个 sql 语句来获取主键,可以指定在插入语句之前(order="before")或之后(order="after")执行。

示例 1:mysql 插入后获取自增 id

<insert id="insertorder">
    <!-- 插入后执行,获取最后插入的 id -->
    <selectkey keyproperty="id" resulttype="int" order="after">
        select last_insert_id()
    </selectkey>
    insert into orders(user_id, amount, status)
    values (#{userid}, #{amount}, #{status})
</insert>

示例 2:oracle 插入前从序列获取 id

<insert id="insertorder">
    <selectkey keyproperty="id" resulttype="long" order="before">
        select orders_seq.nextval from dual
    </selectkey>
    insert into orders(id, user_id, amount, status)
    values (#{id}, #{userid}, #{amount}, #{status})
</insert>

示例 3:使用 uuid 作为主键(插入前生成)

<insert id="insertuser">
    <selectkey keyproperty="id" resulttype="string" order="before">
        select replace(uuid(), '-', '')
    </selectkey>
    insert into users(id, name) values (#{id}, #{name})
</insert>

属性说明

  • keyproperty:目标对象的属性名。
  • resulttype:主键的 java 类型。
  • orderbefore(在插入前执行)或 after(在插入后执行)。

四、流程图:主键回填的执行过程

为了更直观地理解两种方式的执行流程,下面用流程图表示。

1.usegeneratedkeys方式流程

1.usegeneratedkeys方式流程

2.<selectkey order="after">方式流程(以 mysql 为例)

2.<selectkey order="after">方式流程(以 mysql 为例)

3.<selectkey order="before">方式流程(以 oracle 为例)

3.<selectkey order="before">方式流程(以 oracle 为例)

五、两种方式的对比与选择

对比项usegeneratedkeys<selectkey>
配置复杂度极低,两个属性即可稍高,需要写子查询
适用数据库支持自增列的数据库(mysql 等)所有数据库(oracle、sql server 等)
性能高,一次网络交互可能两次交互(after 时)
批量插入支持有限制(需看驱动)一般不用于批量
主键生成时机插入后可自由控制(before / after
非数值主键(uuid)不支持支持

选择建议

  • mysql + 自增主键 → 优先使用 usegeneratedkeys
  • oracle 序列 / uuid / 复合主键 → 使用 <selectkey>
  • 需要插入前生成主键(如分布式 id) → 使用 <selectkey order="before">

六、常见问题与注意事项

1. 为什么插入后对象的 id 还是 null?

  • 检查 usegeneratedkeys="true"keyproperty 是否正确配置。
  • 检查 keyproperty 对应的属性在 java 对象中是否有 setter 方法(mybatis 通过反射赋值)。
  • 确认数据库表确实支持自增列(mysql 需 auto_increment)。

2. 批量插入时能否使用主键回填?

  • mysql 驱动支持批量插入时返回生成的主键,但 mybatis 的 usegeneratedkeys 对批量支持不统一。建议使用循环单条插入,或者使用 <foreach> 配合特殊写法(需要数据库驱动支持)。
  • 简单场景下,可以放弃批量回填,插入后单独查询。

3.last_insert_id()与并发

mysql 的 last_insert_id()连接级别的,每个客户端连接返回自己最后插入的 id,不会受其他并发连接影响,因此是安全的。

4. 主键回填后,对象会被修改吗?

会!mybatis 会直接修改传入的 java 对象的属性值。因此你无需重新接收返回值,直接使用原对象即可。

七、完整示例代码

数据库表(mysql)

create table `orders` (
  `id` int(11) not null auto_increment,
  `user_id` int(11) not null,
  `amount` decimal(10,2) not null,
  `status` tinyint(4) default '0',
  primary key (`id`)
);

java 实体类

public class order {
    private integer id;
    private integer userid;
    private bigdecimal amount;
    private integer status;
    // 省略 getter / setter / tostring
}

mapper 接口

@mapper
public interface ordermapper {
    // 方式一:usegeneratedkeys
    int insertorder(order order);
    // 方式二:selectkey (mysql after)
    int insertorderwithselectkey(order order);
}

xml 映射文件

<!-- 方式一 -->
<insert id="insertorder" usegeneratedkeys="true" keyproperty="id">
    insert into orders(user_id, amount, status)
    values (#{userid}, #{amount}, #{status})
</insert>
<!-- 方式二 -->
<insert id="insertorderwithselectkey">
    <selectkey keyproperty="id" resulttype="int" order="after">
        select last_insert_id()
    </selectkey>
    insert into orders(user_id, amount, status)
    values (#{userid}, #{amount}, #{status})
</insert>

测试代码

@springboottest
class ordermappertest {
    @autowired
    private ordermapper ordermapper;
    @test
    void testinsert() {
        order order = new order();
        order.setuserid(1001);
        order.setamount(new bigdecimal("199.00"));
        order.setstatus(0);
        int rows = ordermapper.insertorder(order);
        system.out.println("影响行数:" + rows);
        system.out.println("回填后的主键:" + order.getid()); // 输出自增 id
    }
}

八、总结

主键回填是 mybatis 中非常实用且高频使用的特性,它解决了插入数据后立即获取主键的痛点。掌握两种实现方式:

  • usegeneratedkeys:简洁高效,mysql 首选。
  • <selectkey>:灵活强大,适配各种数据库及非自增主键场景。

在实际开发中,根据数据库类型和业务需求选择合适的方式,可以大大简化代码逻辑,提升开发效率。

以上就是mybatis主键回填的两种实现方式的详细内容,更多关于mybatis实现主键回填的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com