1. 概述

MyBatis 是一个开源的Java持久层框架,可作为 JDBCHibernate 的替代方案。它帮助我们减少代码量并简化结果检索,使开发者能专注于编写自定义SQL查询或存储过程。

本文将学习如何在使用MyBatis和Spring Boot插入数据时返回数据库自动生成的ID。

2. 依赖配置

首先在 pom.xml 中添加 mybatis-spring-boot-starter 依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

3. 示例搭建

3.1. 定义实体类

先创建一个表示汽车的简单实体类:

public class Car {
    private Long id;

    private String model;

    // getters and setters
}

然后在 car-schema.sql 文件中定义建表语句:

CREATE TABLE IF NOT EXISTS CAR
(
    ID    INTEGER PRIMARY KEY AUTO_INCREMENT,
    MODEL VARCHAR(100) NOT NULL
);

3.2. 配置数据源

使用H2内存数据库配置数据源:

@Bean
public DataSource dataSource() {
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder
      .setType(EmbeddedDatabaseType.H2)
      .setName("testdb")
      .addScript("car-schema.sql")
      .build();
}

@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    return factoryBean.getObject();
}

基础环境搭建完成,接下来通过注解和XML两种方式实现自增ID的获取。

4. 使用注解方式

定义Mapper接口,MyBatis通过它绑定方法和SQL语句:

@Mapper
public interface CarMapper {
    // ...
}

添加基础插入方法:

@Insert("INSERT INTO CAR(MODEL) values (#{model})")
void save(Car car);

⚠️ 常见误区:直接返回 Long 类型并不能获取自增ID,实际返回的是影响行数(成功插入返回1)。

要获取自增ID,需使用 @Options@SelectKey 注解。

4.1. @Options 注解方案

通过 @Options 扩展插入方法:

@Insert("INSERT INTO CAR(MODEL) values (#{model})")
@Options(useGeneratedKeys = true, keyColumn = "ID", keyProperty = "id")
void saveUsingOptions(Car car);

关键属性说明:

  • useGeneratedKeys:启用自增键功能
  • keyColumn:数据库主键列名
  • keyProperty:实体类中接收ID的字段名

✅ 底层原理:MyBatis通过反射将数据库生成的ID值映射到 Car 对象的 id 字段。

测试验证:

@Test
void givenCar_whenSaveUsingOptions_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");

    carMapper.saveUsingOptions(car);

    assertNotNull(car.getId());
}

4.2. @SelectKey 注解方案

使用 @SelectKey 获取自增ID,特别适合使用序列或特殊函数生成ID的场景

⚠️ 注意:当同时存在 @SelectKey@Options 时,前者优先级更高。

实现方法:

@Insert("INSERT INTO CAR(MODEL) values (#{model})")
@SelectKey(statement = "CALL IDENTITY()", before = false, keyColumn = "ID", keyProperty = "id", resultType = Long.class)
void saveUsingSelectKey(Car car);

属性解析:

  • statement:获取ID的SQL语句
  • before:是否在插入前执行(false表示插入后执行)
  • keyColumn:主键列名
  • keyProperty:实体类接收字段
  • resultType:返回值类型

⚠️ H2数据库兼容性:需设置数据库模式为 LEGACY 才能使用 CALL IDENTITY()

"testdb;MODE=LEGACY"

测试验证:

@Test
void givenCar_whenSaveUsingSelectKey_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");

    carMapper.saveUsingSelectKey(car);

    assertNotNull(car.getId());
}

5. 使用XML方式

5.1. 定义XML Mapper

创建 CarXmlMapper 接口:

@Mapper
public interface CarXmlMapper {
     // ...
}

在XML文件中定义SQL映射(namespace 需指定接口全路径):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.baeldung.mybatis.generatedid.CarXmlMapper">

</mapper>

5.2. useGeneratedKeys 属性方案

在接口中添加方法:

void saveUsingOptions(Car car);

XML中定义插入语句:

<insert id="saveUsingOptions" parameterType="com.baeldung.mybatis.generatedid.Car"
  useGeneratedKeys="true" keyColumn="ID" keyProperty="id">
    INSERT INTO CAR(MODEL)
    VALUES (#{model});
</insert>

属性说明:

  • id:绑定接口方法名
  • parameterType:参数类型
  • useGeneratedKeys:启用自增键
  • keyColumn:主键列名
  • keyProperty:实体类接收字段

测试验证:

@Test
void givenCar_whenSaveUsingOptions_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");

    carXmlMapper.saveUsingOptions(car);

    assertNotNull(car.getId());
}

5.3. selectKey 元素方案

接口添加方法:

void saveUsingSelectKey(Car car);

XML中使用 selectKey 元素:

<insert id="saveUsingSelectKey" parameterType="com.baeldung.mybatis.generatedid.Car">
    INSERT INTO CAR(MODEL)
    VALUES (#{model});

    <selectKey resultType="Long" order="AFTER" keyColumn="ID" keyProperty="id">
        CALL IDENTITY()
    </selectKey>
</insert>

元素属性:

  • resultType:返回值类型
  • order:执行时机(AFTER表示插入后执行)
  • keyColumn:主键列名
  • keyProperty:实体类接收字段

测试验证:

@Test
void givenCar_whenSaveUsingSelectKey_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");

    carXmlMapper.saveUsingSelectKey(car);

    assertNotNull(car.getId());
}

6. 总结

本文介绍了在MyBatis和Spring中获取插入数据自增ID的两种实现方式:

注解方案

  • @Options:简单直接,适合标准自增列
  • @SelectKey:灵活强大,支持复杂ID生成策略

XML方案

  • useGeneratedKeys:与注解版功能一致
  • selectKey:提供更细粒度的控制

完整代码示例可在 GitHub仓库 获取。


原始标题:Return Auto Generated ID From Insert With MyBatis and Spring | Baeldung