MyBatis 插入(INSERT)数据时返回自动生成的 ID
1、概览
MyBatis 是一个开源 Java 持久层框架,可作为 JDBC 和 Hibernate 的替代品。它能简化持久层的代码,并自动封装结果集,开发者只需专注于编写自定义 SQL 查询或存储过程。
本文将带你了解如何在 Spring 中使用 MyBatis 插入(INSERT)数据时返回自动生成的 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、定义实体
首先,创建一个代表汽车的简单实体类 Car:
public class Car {
private Long id;
private String model;
// Getter / Setter 方法省略
}
其次,定义一条创建表的 SQL 语句,并将其放入 car-schema.sql 文件中:
CREATE TABLE IF NOT EXISTS CAR
(
ID INTEGER PRIMARY KEY AUTO_INCREMENT,
MODEL VARCHAR(100) NOT NULL
);
如上,ID 列是自增自主键(AUTO_INCREMENT)。
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("INSERT INTO CAR(MODEL) values (#{model})")
void save(Car car);
直观感觉上,我们可能会在 Mapper 方法上直接返回 Long,认为 MyBatis 会返回实体的自增 ID。然而,并不会,Mapper 接口返回的 Long / Integer 表示受影响的行数,并不是 INSERT 语句的自增 ID。
要检索生成的 ID,可以使用 @Options 或 @SelectKey 注解。
4.1、@Options 注解
使用 @Options 注解来扩展 insert 语句:
@Insert("INSERT INTO CAR(MODEL) values (#{model})")
@Options(useGeneratedKeys = true, keyColumn = "ID", keyProperty = "id")
void saveUsingOptions(Car car);
如上,设置了三个属性:
useGeneratedKeys- 是否要启动自动生成 KEY 功能。keyColumn- 表示 KEY 所在列的名称(表中的自增列名)。keyProperty- 表示保存自动生成的 KYE 值的字段名称(对象中的字段名)。
如果有多个自增列的话,可以使用逗号分割。
在底层,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 注解
另一种返回 ID 自增值的方法是使用 @SelectKey 注解。当我们想要使用序列或 ID 生成函数来检索 ID 时,这个注解非常有用。
如果用 @SelectKey 注解 Mapper 方法,MyBatis 就会忽略 @Options 等注解。
在 CarMapper 中创建一个新方法,在执行插入后检索 ID 值:
@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- 定义在INSERT语句后执行的语句。before- 语句是在INSERT之前还是之后执行。keyColumn- KEY 所在列的名称(表中的自增列名)。keyProperty- 保存自增 KEY 值的字段名称。resultType- 表示keyProperty的类型
注意,H2 数据库中的 IDENTITY() 函数已被删除。更多详情请点击 此处。
为了能在 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());
}
5、使用 XML
现在,来看看如何在基于 XML 的方式中返回自增 ID。
首先,定义 CarXmlMapper 接口:
@Mapper
public interface CarXmlMapper {
// ...
}
与基于注解的方法不同,我们不会直接在 Mapper 接口中编写 SQL 语句。相反,我们需要定义 Mapper 的 XML 文件,并将所有查询定义在其中:
<?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>
注意,namespace 属性值需要指定 CarXmlMapper Mapper 接口的全路径。
5.1、UseGeneratedKeys 属性
接下来,在 CarXmlMapper 接口中定义一个方法:
void saveUsingOptions(Car car);
然后,在 Mapper XML 中定义 INSERT 语句,通过 insert 节点的 id 属性将其映射到 CarXmlMapper 接口中的 saveUsingOptions() 方法:
<insert id="saveUsingOptions" parameterType="com.baeldung.mybatis.generatedid.Car"
useGeneratedKeys="true" keyColumn="ID" keyProperty="id">
INSERT INTO CAR(MODEL)
VALUES (#{model});
</insert>
insert 节点上定义的属性如下:
id- 将查询绑定至CarXmlMapper类中的指定方法。parameterType-saveUsingOptions()方法参数的类型。useGeneratedKeys- 表示要使用 ID 自动生成功能。keyColumn- 指定自增 ID 的列名。keyProperty- 指定 Car 对象中保存自增 ID 值的字段名称。
测试如下:
@Test
void givenCar_whenSaveUsingOptions_thenReturnId() {
Car car = new Car();
car.setModel("BMW");
carXmlMapper.saveUsingOptions(car);
assertNotNull(car.getId());
}
5.2、SelectKey 元素
接下来,在 CarXmlMapper 接口中添加一个新方法,这次使用 selectKey 元素检索自增 ID:
void saveUsingSelectKey(Car car);
们在 Mapper XML 文件中指定语句,并将其绑定到 Mapper 接口方法:
<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>
如上,在 insert 元素中定义了 selectKey 元素,其属性如下:
resultType- 指定语句返回的类型。order- 应在插入语句之前还是之后调用CALL IDENTITY()语句。keyColumn- 表示 ID 列的名称。keyProperty- 保存 ID 自增列值的属性名称。
最后,测试如下:
@Test
void givenCar_whenSaveUsingSelectKey_thenReturnId() {
Car car = new Car();
car.setModel("BMW");
carXmlMapper.saveUsingSelectKey(car);
assertNotNull(car.getId());
}
6、总结
本文介绍了如何在 Spring 中使用 MyBatis 插入(INSERT)数据时返回自动生成的 ID,主要介绍了使用 @Options 和 @SelectKey 注解以及 XML 的方式。
Ref:https://www.baeldung.com/spring-mybatis-return-auto-generated-id