1、概览 {#1概览}
MyBatis 是一个开源 Java 持久层框架,可作为 JDBC 和 Hibernate 的替代品。它能简化持久层的代码,并自动封装结果集,开发者只需专注于编写自定义 SQL 查询或存储过程。
本文将带你了解如何在 Spring 中使用 MyBatis 插入(INSERT
)数据时返回自动生成的 ID。
2、依赖设置 {#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示例}
先创建一个简单的示例,在整个文章中都会用到。
3.1、定义实体 {#31定义实体}
首先,创建一个代表汽车的简单实体类 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、定义数据源 {#32定义数据源}
使用 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、使用注解 {#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 注解 {#41options-注解}
使用 @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 注解 {#42selectkey-注解}
另一种返回 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 {#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 属性 {#51usegeneratedkeys-属性}
接下来,在 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 元素 {#52selectkey-元素}
接下来,在 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、总结 {#6总结}
本文介绍了如何在 Spring 中使用 MyBatis 插入(INSERT
)数据时返回自动生成的 ID,主要介绍了使用 @Options
和 @SelectKey
注解以及 XML 的方式。
Ref:https://www.baeldung.com/spring-mybatis-return-auto-generated-id