java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis插入数据时获取生成主键

MyBatis插入数据时获取生成主键的两种方法

作者:步行cgn

在数据库设计中,主键自增是最常见的场景,当我们在代码中插入一条数据时,往往需要立即获取这条数据在数据库中生成的唯一主键 ID,MyBatis 提供了两种强大的机制来实现这一点,本文将从用法、底层原理到常见陷阱为你全面拆解,需要的朋友可以参考下

引言

在数据库设计中,主键自增(如 MySQL 的 AUTO_INCREMENT)是最常见的场景。当我们在代码中插入一条数据时,往往需要立即获取这条数据在数据库中生成的唯一主键 ID(例如用于后续关联操作或返回给前端)。

MyBatis 提供了两种强大的机制来实现这一点:useGeneratedKeys + keyProperty(推荐)和 <selectKey> 标签。下面我将从用法、底层原理到常见陷阱为你全面拆解。

一、方法一:useGeneratedKeys+keyProperty(最推荐)

这是 MyBatis 最简洁、最直接的方式,利用 JDBC 底层的 Statement.getGeneratedKeys() 方法实现。它适用于支持自动生成主键的数据库(MySQL、SQL Server、PostgreSQL 等)。

1. 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>

注意:这里无需在 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. 底层工作原理

  1. MyBatis 执行 PreparedStatement 时,会调用 Connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)。
  2. 执行 SQL 后,JDBC 驱动通过 ResultSet generatedKeys = statement.getGeneratedKeys() 获取生成的主键结果集。
  3. 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 属性控制查询语句的执行时机。

三、特殊情况处理:批量插入时获取主键

当进行批量插入时,依然可以使用 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

keyProperty 写错属性名

XML 中的 parameterType 与 keyProperty 的关系

数据库驱动版本兼容性

<selectKey> 与 resultType

五、总结对比

特征useGeneratedKeys + keyProperty<selectKey> 标签
适用数据库MySQL、SQL Server、PostgreSQL(自增/Identity)所有数据库(包括 Oracle、UUID 等)
配置复杂度极简(仅两个属性)稍复杂(需配置查询语句和顺序)
获取时机执行 INSERT 之后可自定义 BEFORE 或 AFTER
底层依赖JDBC 的 getGeneratedKeys() 方法执行额外的 SQL 查询语句
推荐指数⭐⭐⭐⭐⭐(首选)⭐⭐⭐⭐(仅用于 Oracle、UUID 等特殊场景)

六、最佳实践总结

  1. 使用 useGeneratedKeys 是最优雅的方式,它能确保你的代码与数据库驱动紧密结合,且仅执行一次 SQL,性能最优。
  2. 在你的 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>
  1. 运行代码后,直接调用 car.getId() 即可拿到主键,彻底告别 SELECT LAST_INSERT_ID() 的繁琐操作!

以上就是MyBatis插入数据时获取生成主键的两种方法的详细内容,更多关于MyBatis插入数据时获取生成主键的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文