ZetCode

Spring JdbcClient

最后修改日期:2025 年 6 月 6 日

本教程演示如何使用 Spring 的新 JdbcClient 进行数据库访问。

JdbcClient 是 Spring Framework 6.1 中引入的一个新的流畅 API。 它简化了 JDBC 操作,同时保持了灵活性。 它提供了一种比 JdbcTemplate 更现代的替代方案,并具有更直观的 API。

我们将在所有示例中使用 H2 内存数据库。 JdbcClient 适用于任何符合 JDBC 的数据库。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.2.7</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>2.3.232</version>
    </dependency>
</dependencies>

添加以下依赖项以将 Spring JdbcClient 与 H2 数据库一起使用。

基本 CRUD 操作

此示例演示了基本的创建、读取、更新和删除操作。

Main.java
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;
import java.util.List;

record User(String name, String email) {}

void main() {
    DataSource dataSource = new DriverManagerDataSource(
        "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
    JdbcClient jdbClient = JdbcClient.create(dataSource);

    // Create table
    jdbClient.sql("""
            CREATE TABLE users (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255),
                email VARCHAR(255)
            )
        """).update();

    // Insert data
    jdbClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .param("name", "Alice")
            .param("email", "alice@example.com")
            .update();
            
    jdbClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .param("name", "Paul")
            .param("email", "paul@example.com")
            .update();

    // Fetch data
    List<User> users = jdbClient.sql("SELECT name, email FROM users")
            .query(User.class)
            .list();

    users.forEach(System.out::println);
}

该示例创建了一个表,插入了记录,然后查询它们。

DataSource dataSource = new DriverManagerDataSource(
    "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");

我们创建了一个 H2 内存数据源。 DB_CLOSE_DELAY=-1 在连接关闭后将数据库保留在内存中。

JdbcClient jdbClient = JdbcClient.create(dataSource);

使用数据源创建 JdbcClient 实例。 JdbcClient 是线程安全的。

jdbClient.sql("CREATE TABLE...").update();

执行 DDL 语句以创建表。 update() 方法执行 SQL。

jdbClient.sql("INSERT...").param("name", "Alice")...update();

使用命名参数插入数据。 参数被安全地绑定以防止 SQL 注入。

List<User> users = jdbClient.sql("SELECT...").query(User.class).list();

查询记录并将它们映射到 User 记录。 JdbcClient 自动将列名映射到记录组件。

查询单个记录

此示例演示了查询带有可选结果的单个记录。

Main.java
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;
import java.util.Optional;

record User(String name, String email) {}

void main() {
    DataSource dataSource = new DriverManagerDataSource(
        "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
    JdbcClient jdbcClient = JdbcClient.create(dataSource);

    // Create table
    jdbcClient.sql("""
            CREATE TABLE users (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255),
                email VARCHAR(255)
            )
        """).update();

    // Insert data
    jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .param("name", "Alice")
            .param("email", "alice@example.com")
            .update();

    // Fetch single record by ID
    Optional<User> user = jdbcClient.sql("SELECT name, email FROM users WHERE id = :id")
            .param("id", 1L)
            .query(User.class)
            .optional();

    user.ifPresent(System.out::println);
}

该示例查询一个包含在 Optional 中的单个用户记录。

Optional<User> user = jdbcClient.sql("SELECT...").param("id", 1L)
        .query(User.class).optional();

在查询可能不存在的单个记录时使用 optional()。 如果未找到结果,它将返回 Optional.empty()。

user.ifPresent(System.out::println);

我们安全地处理 Optional 结果。 仅当存在记录时才打印该记录。

将 ParamSource 与 Records 一起使用

此示例演示了如何将 records 用作参数源。

Main.java
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;
import java.util.List;

record User(Long id, String name, String email) {}

void main() {
    DataSource dataSource = new DriverManagerDataSource(
        "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
    JdbcClient jdbcClient = JdbcClient.create(dataSource);

    // Create table
    jdbcClient.sql("""
            CREATE TABLE users (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255),
                email VARCHAR(255)
            )
        """).update();

    // Insert using paramSource with a record
    User newUser = new User(null, "Mark", "mark@example.com");
    int updated = jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .paramSource(newUser)
            .update();

    System.out.println("Rows updated: " + updated);

    // Fetch data
    List<User> users = jdbcClient.sql("SELECT * FROM users")
            .query(User.class)
            .list();

    users.forEach(System.out::println);
}

该示例演示了使用 record 作为参数源。

User newUser = new User(null, "Mark", "mark@example.com");

创建一个新的 User record。 id 为 null,因为它将自动生成。

jdbcClient.sql("INSERT...").paramSource(newUser).update();

使用 paramSource 将 record 属性绑定到 SQL 参数。 属性名称必须与参数名称匹配。

List<User> users = jdbcClient.sql("SELECT * FROM users")
        .query(User.class)
        .list();

查询所有用户并映射到 User 记录。 id 列自动映射到记录组件。

计数记录

此示例演示了如何执行标量查询,如 count。

Main.java
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

record User(String name, String email) {}

void main() {
    DataSource dataSource = new DriverManagerDataSource(
        "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
    JdbcClient jdbcClient = JdbcClient.create(dataSource);

    // Create table
    jdbcClient.sql("""
            CREATE TABLE users (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255),
                email VARCHAR(255)
            )
        """).update();

    // Insert data
    jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .param("name", "Alice")
            .param("email", "alice@example.com")
            .update();

    jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
            .param("name", "Paul")
            .param("email", "paul@example.com")
            .update();

    // Count records
    Integer count = jdbcClient.sql("SELECT COUNT(*) FROM users")
            .query(Integer.class)
            .single();

    System.out.println("Total users: " + count);
}

该示例统计 users 表中的记录。

Integer count = jdbcClient.sql("SELECT COUNT(*) FROM users")
        .query(Integer.class)
        .single();

对于标量查询,指定返回类型并使用 single。 它期望只有一行,一列。

System.out.println("Total users: " + count);

打印计数结果。 该查询返回表中的行数。

高级查询

此示例演示了更高级的查询技术。

Main.java
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;

record User(Long id, String name, String email) {}

void main() {
    DataSource dataSource = new DriverManagerDataSource(
        "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
    JdbcClient jdbcClient = JdbcClient.create(dataSource);

    // Create table
    jdbcClient.sql("""
            CREATE TABLE users (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255),
                email VARCHAR(255)
            )
        """).update();

    // Insert sample data
    List.of(
        new User(null, "Alice", "alice@example.com"),
        new User(null, "Bob", "bob@example.com"),
        new User(null, "Charlie", "charlie@example.com")
    ).forEach(user -> {
        jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
                .paramSource(user)
                .update();
    });

    // Query with filtering and sorting
    List<User> filteredUsers = jdbcClient.sql("""
            SELECT id, name, email FROM users 
            WHERE name LIKE :pattern 
            ORDER BY name DESC
        """)
        .param("pattern", "A%")
        .query(User.class)
        .list();

    System.out.println("Filtered users:");
    filteredUsers.forEach(System.out::println);

    // Query to Map
    List<Map<String, Object>> usersAsMaps = jdbcClient.sql("SELECT * FROM users")
            .query()
            .listOfRows();

    System.out.println("\nUsers as maps:");
    usersAsMaps.forEach(System.out::println);
}

该示例显示了过滤查询和映射到不同的结果类型。

List<User> filteredUsers = jdbcClient.sql("SELECT...WHERE name LIKE :pattern")
        .param("pattern", "A%")
        .query(User.class)
        .list();

使用带有命名参数的 SQL WHERE 子句过滤记录。 该查询仅返回名称以“A”开头的用户。

List<Map<String, Object>> usersAsMaps = jdbcClient.sql("SELECT * FROM users")
        .query()
        .listOfRows();

当未指定特定映射时,查询可以将结果作为 Map 返回。 每行都成为列名到值的 Map。

usersAsMaps.forEach(System.out::println);

打印每行的 map 表示形式。 用于调试或在事先不知道结构时很有用。

来源

JdbcClient 参考

在本教程中,我们探讨了 Spring 的 JdbcClient 以进行数据库访问。 它为常见的 JDBC 操作提供了一个现代的、流畅的 API。

作者

我叫 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程