使用 R2DBC 访问数据

本指南将引导您完成构建应用程序的过程,该应用程序使用 Spring Data R2DBC 通过响应式数据库驱动程序在关系数据库中存储和检索数据。

您将构建什么

您将构建一个应用程序,该应用程序将 `Customer` POJO(普通旧 Java 对象)存储在基于内存的数据库中。

您需要什么

如何完成本指南

与大多数 Spring 入门指南 一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的 basic 设置步骤。无论哪种方式,您最终都会得到可运行的代码。

要**从头开始**,请继续转到 使用 Spring Initializr 开始

要**跳过基础步骤**,请执行以下操作

**完成后**,您可以将您的结果与 ` {project_id}/complete` 中的代码进行比较。

使用 Spring Initializr 开始

您可以使用此 预初始化项目 并单击“生成”以下载 ZIP 文件。此项目已配置为适合本教程中的示例。

要手动初始化项目

  1. 导航到 https://start.spring.io。此服务将引入应用程序所需的所有依赖项,并为您完成大部分设置工作。

  2. 选择 Gradle 或 Maven 以及您要使用的语言。本指南假设您选择了 Java。

  3. 单击**依赖项**并选择**Spring Data R2DBC** 和**H2 数据库**。

  4. 单击**生成**。

  5. 下载生成的 ZIP 文件,它是使用您的选择配置的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,您可以通过 IDE 完成此过程。
您也可以从 Github 分叉项目并在您的 IDE 或其他编辑器中打开它。

定义模式

在此示例中,您存储 `Customer` 对象,每个对象都用作 R2DBC 实体进行注释。以下清单显示了 SQL 模式类(位于 `src/main/resources/schema.sql` 中)

CREATE TABLE IF NOT EXISTS customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255));

这里有一个包含三列的 `customer` 表:`id`、`first_name` 和 `last_name`。`id` 列是自动递增的,其他列遵循默认的蛇形命名方案。稍后,我们需要注册一个 `ConnectionFactoryInitializer` 以在应用程序启动期间拾取 `schema.sql` 文件以初始化数据库模式。由于 H2 驱动程序位于类路径上并且我们没有指定连接 URL,因此 Spring Boot 启动一个嵌入式 H2 数据库。

定义简单的实体

在此示例中,您存储 `Customer` 对象,每个对象都用作 R2DBC 实体进行注释。以下清单显示了 Customer 类(位于 `src/main/java/com/example/accessingdatar2dbc/Customer.java` 中)

package com.example.accessingdatar2dbc;

import org.springframework.data.annotation.Id;

public class Customer {

    @Id
    private Long id;

    private final String firstName;

    private final String lastName;

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    @Override
    public String toString() {
        return String.format(
            "Customer[id=%d, firstName='%s', lastName='%s']",
            id, firstName, lastName);
    }
}

这里有一个 `Customer` 类,它具有三个属性:`id`、`firstName` 和 `lastName`。`Customer` 类进行了最少的注释。`id` 属性用 `@Id` 注释,以便 Spring Data R2DBC 可以识别主键。默认情况下,主键假定在 `INSERT` 时由数据库生成。

其他两个属性 `firstName` 和 `lastName` 没有注释。假设它们映射到与属性本身名称相同的列。

方便的 `toString()` 方法打印出客户的属性。

创建简单的查询

Spring Data R2DBC 专注于使用 R2DBC 作为底层技术在关系数据库中存储数据。其最引人注目的功能是能够在运行时从存储库接口创建存储库实现。

要了解其工作原理,请创建一个使用 `Customer` 实体的存储库接口,如下清单(位于 `src/main/java/com/example/accessingdatar2dbc/CustomerRepository.java` 中)所示

package com.example.accessingdatar2dbc;

import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import reactor.core.publisher.Flux;

public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {

    @Query("SELECT * FROM customer WHERE last_name = :lastname")
    Flux<Customer> findByLastName(String lastName);

}

`CustomerRepository` 扩展了 `ReactiveCrudRepository` 接口。它使用的实体和 ID 类型(`Customer` 和 `Long`)在 `ReactiveCrudRepository` 的泛型参数中指定。通过扩展 `ReactiveCrudRepository`,`CustomerRepository` 继承了几个用于处理 `Customer` 持久化的方法,包括使用响应式类型保存、删除和查找 `Customer` 实体的方法。

Spring Data R2DBC 还允许您通过使用 `@Query` 注释来定义其他查询方法。例如,`CustomerRepository` 包含 `findByLastName()` 方法。

在典型的 Java 应用程序中,您可能希望编写一个实现 `CustomerRepository` 的类。但是,这就是 Spring Data R2DBC 如此强大的原因:您无需编写存储库接口的实现。运行应用程序时,Spring Data R2DBC 会创建一个实现。

现在您可以连接此示例并查看其外观!

创建应用程序类

Spring Initializr 为应用程序创建一个简单的类。以下清单显示了 Initializr 为此示例创建的类(位于 `src/main/java/com/example/accessingdatar2dbc/AccessingDataR2dbcApplication.java` 中)

package com.example.accessingdatar2dbc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataR2dbcApplication {

	public static void main(String[] args) {
		SpringApplication.run(AccessingDataR2dbcApplication.class, args);
	}

}

`@SpringBootApplication` 是一个方便的注释,它添加了以下所有内容

  • `@Configuration`:将类标记为应用程序上下文的 bean 定义的来源。

  • `@EnableAutoConfiguration`:告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果 `spring-webmvc` 位于类路径中,则此注释将应用程序标记为 Web 应用程序并激活关键行为,例如设置 `DispatcherServlet`。

  • `@ComponentScan`:告诉 Spring 在 `com/example` 包中查找其他组件、配置和服务,使其能够找到控制器。

`main()` 方法使用 Spring Boot 的 `SpringApplication.run()` 方法启动应用程序。您是否注意到没有一行 XML?也没有 `web.xml` 文件。此 Web 应用程序是 100% 纯 Java,您无需处理任何管道或基础设施的配置。

现在您需要修改 Initializr 为您创建的简单类。要获得输出(在此示例中为控制台输出),您需要设置一个日志记录器。然后,您需要设置初始化程序以设置模式和一些数据,并使用它来生成输出。以下清单显示了完成的 `AccessingDataR2dbcApplication` 类(位于 `src/main/java/com/example/accessingdatar2dbc/AccessingDataR2dbcApplication.java` 中)

package com.example.accessingdatar2dbc;

import io.r2dbc.spi.ConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;

import java.time.Duration;
import java.util.Arrays;

@SpringBootApplication
public class AccessingDataR2dbcApplication {

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

    public static void main(String[] args) {
        SpringApplication.run(AccessingDataR2dbcApplication.class, args);
    }

    @Bean
    public CommandLineRunner demo(CustomerRepository repository) {

        return (args) -> {
            // save a few customers
            repository.saveAll(Arrays.asList(new Customer("Jack", "Bauer"),
                new Customer("Chloe", "O'Brian"),
                new Customer("Kim", "Bauer"),
                new Customer("David", "Palmer"),
                new Customer("Michelle", "Dessler")))
                .blockLast(Duration.ofSeconds(10));

            // fetch all customers
            log.info("Customers found with findAll():");
            log.info("-------------------------------");
            repository.findAll().doOnNext(customer -> {
                log.info(customer.toString());
            }).blockLast(Duration.ofSeconds(10));

            log.info("");

            // fetch an individual customer by ID
			repository.findById(1L).doOnNext(customer -> {
				log.info("Customer found with findById(1L):");
				log.info("--------------------------------");
				log.info(customer.toString());
				log.info("");
			}).block(Duration.ofSeconds(10));


            // fetch customers by last name
            log.info("Customer found with findByLastName('Bauer'):");
            log.info("--------------------------------------------");
            repository.findByLastName("Bauer").doOnNext(bauer -> {
                log.info(bauer.toString());
            }).blockLast(Duration.ofSeconds(10));;
            log.info("");
        };
    }

}

`AccessingDataR2dbcApplication` 类包含一个 `main()` 方法,该方法对 `CustomerRepository` 进行了一些测试。首先,它从 Spring 应用程序上下文获取 `CustomerRepository`。然后,它保存少量 `Customer` 对象,演示 `save()` 方法并设置一些要使用的数据。接下来,它调用 `findAll()` 从数据库中获取所有 `Customer` 对象。然后,它调用 `findById()` 通过其 ID 获取单个 `Customer`。最后,它调用 `findByLastName()` 查找所有姓氏为“Bauer”的客户。

R2DBC 是一种响应式编程技术。同时,我们在同步的命令式流程中使用它,这就是为什么我们需要使用 `block(...)` 方法的变体来同步每个调用的原因。在一个典型的响应式应用程序中,生成的 `Mono` 或 `Flux` 将表示一个操作符管道,该管道将返回给订阅响应式序列而不阻塞调用线程的 Web 控制器或事件处理器。

默认情况下,Spring Boot 启用 R2DBC 存储库支持,并在 `@SpringBootApplication` 所在的包(及其子包)中查找。如果您的配置在不可见的包中具有 R2DBC 存储库接口定义,则可以使用 `@EnableR2dbcRepositories` 及其类型安全的 `basePackageClasses=MyRepository.class` 参数指出备用包。

构建可执行 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需依赖项、类和资源的单个可执行 JAR 文件并运行该文件。构建可执行 jar 可以轻松地在整个开发生命周期中、跨不同环境等将服务作为应用程序进行交付、版本控制和部署。

如果您使用 Gradle,可以使用 `./gradlew bootRun` 运行应用程序。或者,您可以使用 `./gradlew build` 构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar build/libs/{project_id}-0.1.0.jar

如果您使用 Maven,可以使用 `./mvnw spring-boot:run` 运行应用程序。或者,您可以使用 `./mvnw clean package` 构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar target/{project_id}-0.1.0.jar
此处描述的步骤创建了一个可运行的 JAR。您还可以构建一个经典的 WAR 文件

运行应用程序时,您应该看到类似于以下内容的输出

== Customers found with findAll():
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

== Customer found with findOne(1L):
Customer[id=1, firstName='Jack', lastName='Bauer']

== Customer found with findByLastName('Bauer'):
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

总结

恭喜!您已经编写了一个简单的应用程序,它使用 Spring Data R2DBC 将对象保存到数据库并从中提取对象,而无需编写具体的存储库实现。

另请参阅

以下指南可能也有帮助

想编写新的指南或为现有指南做出贡献?请查看我们的 贡献指南

所有指南均采用 ASLv2 许可证发布代码,并采用 署名-非衍生作品创作共用许可证 发布文本。

获取代码