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。

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

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

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

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