MyBatis从插入语句中检索自动生成的ID

MyBatis是一个开源 Java 持久层框架,可以作为JDBC和Hibernate的替代品。它可以帮助我们减少代码并简化结果的检索,让我们专注于编写自定义 SQL 查询或存储过程。

在本教程中,我们将学习如何使用 MyBatis 和 Spring Boot 插入数据时返回自动生成的 ID。

依赖设置 在开始之前,让我们在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>

让我们首先创建一个将在整篇文章中使用的简单示例。

1. 定义实体 首先,让我们创建一个代表汽车的简单实体类:

public class Car {
    private Long id;
    private String model;
    // getters and setters
}
其次,我们定义一个创建表的SQL语句,并将其放在car-schema.sql文件中:

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

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 的方法来检索自动生成的身份。

使用注释 让我们定义Mapper,它代表 MyBatis 用于将方法绑定到相应 SQL 语句的接口:

@Mapper
public interface CarMapper {
    // ...
}
接下来我们添加一个插入语句:

@Insert("INSERT INTO CAR(MODEL) values (#{model})")
void save(Car car);
直觉地,我们可能倾向于返回Long并期望 MyBatis 返回所创建实体的 ID。然而,这并不准确。如果我们这样做,它会返回1,表示插入语句成功。

要检索生成的 ID,我们可以使用@Options或@SelectKey注释。

1. @Options注释 我们可以扩展插入语句的一种方法是使用@Options注释:

@Insert("INSERT INTO CAR(MODEL) values (#{model})")
@Options(useGeneratedKeys = true, keyColumn = "ID", keyProperty = "id")
void saveUsingOptions(Car car);
这里我们设置了三个属性:
  • useGeneratedKeys – 表示我们是否要使用生成的密钥功能
  • keyColumn – 设置包含键的列的名称
  • keyProperty – 表示保存键值的字段名称
此外,我们可以用逗号分隔来指定多个关键属性。

在后台,MyBatis 使用反射将 ID 列的值映射到Car对象的id字段中。

接下来,让我们创建一个测试来确认一切是否按预期进行:

@Test
void givenCar_whenSaveUsingOptions_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");
    carMapper.saveUsingOptions(car);
    assertNotNull(car.getId());
}

2. @SelectKey注释 返回 ID 的另一种方法是使用@SelectKey注释。当我们想要使用序列或身份函数来检索标识符时,此注释非常有用。

此外,如果我们使用@SelectKey注释修饰我们的方法,MyBatis 会忽略诸如@Options之类的注释。

让我们在CarMapper内部创建一个新方法来检索插入后的标识值:

@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 – 保存将在插入语句之后执行的语句
  • before – 表示语句应该在插入之前还是之后执行
  • keyColumn – 保存代表键的列的名称
  • keyProperty – 指定将保存语句返回的值的字段的名称
  • resultType – 表示keyProperty的类型
此外,我们应该注意到IDENTITY()函数已从 H2 数据库中删除。更多详细信息请参见此处。

为了能够在 H2 数据库上执行CALL IDENTITY(),我们需要将模式设置为LEGACY:

"testdb;MODE=LEGACY"
让我们测试一下我们的方法来确认它能正常工作:

@Test
void givenCar_whenSaveUsingSelectKey_thenReturnId() {
    Car car = new Car();
    car.setModel("BMW");
    carMapper.saveUsingSelectKey(car);
    assertNotNull(car.getId());
}