JdbcClient 返回自增 ID

JdbcClient 是 Spring 6.1 引入的一个 Jdbc 客户端工具类,提供了 Fluent 链式调用风格的查询和更新方法,支持 JDBC 风格的位置参数和 Spring 风格的命名参数绑定。

本文将带你了解,如何在使用 JdbcClient 执行 insert 操作时返回自增 ID。

关于 JdbcClient 更多详细的用法可以参阅 “Spring 6 JdbcClient API 指南” 和 “Spring Boot 中的新 JDBC 客户端: JdbcClient”

创建数据表

在本地 MYSQL 数据库 demo 中创建一张简单的 t_user 表,如下:

CREATE TABLE `t_user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `enabled` tinyint unsigned NOT NULL COMMENT '是否启用。0:禁用,1:启用',
  `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户';

其中, id 列是自增列。

Spring Boot 项目设置

创建一个简单的 Spring Boot 应用,添加 spring-boot-starter-jdbc 依赖以及 MYSQL 的驱动:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

然后,在 application.yaml 中配置数据源信息:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&allowMultiQueries=true
    username: root
    password: root

spring-boot-starter-jdbc 模块会自动使用配置的数据源来创建 JdbcClient Bean,不需要自己手动实例化。

JdbcClient 获取自增 ID

和 JdbcTemplate 一样, JdbcClient 也通过 KeyHolder 接口来获取到 insert 记录的自增 ID。

package cn.springdoc.demo.test;

import java.time.LocalDateTime;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    static final Logger log = LoggerFactory.getLogger(DemoApplicationTests.class);

    @Autowired
    JdbcClient jdbcClient;

    @Test
    public void test() throws Exception {
        
        // 创建 KeyHolder 实现
        KeyHolder keyHolder = new GeneratedKeyHolder();
        
        int ret = this.jdbcClient.sql("INSERT INTO `demo`.`t_user` (`id`, `create_at`, `enabled`, `name`) VALUES (:id, :create_at, :enabled, :name);")
                        .param("id", null) // 自增列的值设置为 null
                        .param("create_at", LocalDateTime.now())
                        .param("enabled", Boolean.TRUE)
                        .param("name", "springdoc.cn")
                        .update(keyHolder) // 执行 insert,传递 KeyHolder 实现
                        ;
        
        // 从 keyHolder 获取到自增ID
        long id = keyHolder.getKey().longValue();
        
        log.info("受影响的行数:{}", ret);
        
        log.info("自增的ID:{}", id);
    }
}

如上,首先在测试类中注入 spring-data-jdbc 自动配置的 JdbcClient 实例。

然后实例化 KeyHolder 接口的实现 GeneratedKeyHolder,用于获取自增的 ID。

调用 jdbcClient 的 sql 方法,传递 INSERT 语句,使用命名参数占位符,接着调用 param 方法依次按照参数名称设置参数值。然后调用 update 方法,传递 KeyHolder 实现并执行 INSERT SQL 语句。

最后返回受影响的行数。插入记录的自增 ID 可以通过 keyHolder 接口的 getKey() 方法获取到。

执行该测试,控制台输出如下:

c.s.demo.test.DemoApplicationTests       : 受影响的行数:1
c.s.demo.test.DemoApplicationTests       : 自增的ID:1