CREATE TABLE IF NOT EXISTS customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255));
使用 R2DBC 访问数据
本指南将引导您完成构建应用程序的过程,该应用程序使用 Spring Data R2DBC 通过响应式数据库驱动程序在关系数据库中存储和检索数据。
您将构建什么
您将构建一个应用程序,该应用程序将 `Customer` POJO(普通旧 Java 对象)存储在基于内存的数据库中。
您需要什么
-
大约 15 分钟
-
您喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
-
您也可以将代码直接导入您的 IDE
如何完成本指南
与大多数 Spring 入门指南 一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的 basic 设置步骤。无论哪种方式,您最终都会得到可运行的代码。
要**从头开始**,请继续转到 使用 Spring Initializr 开始。
要**跳过基础步骤**,请执行以下操作
-
下载 并解压本指南的源代码库,或使用 Git 克隆它:
git clone https://github.com/spring-guides/{project_id}.git
-
进入 ` {project_id}/initial` 目录
-
跳至 定义模式。
**完成后**,您可以将您的结果与 ` {project_id}/complete` 中的代码进行比较。
使用 Spring Initializr 开始
您可以使用此 预初始化项目 并单击“生成”以下载 ZIP 文件。此项目已配置为适合本教程中的示例。
要手动初始化项目
-
导航到 https://start.spring.io。此服务将引入应用程序所需的所有依赖项,并为您完成大部分设置工作。
-
选择 Gradle 或 Maven 以及您要使用的语言。本指南假设您选择了 Java。
-
单击**依赖项**并选择**Spring Data R2DBC** 和**H2 数据库**。
-
单击**生成**。
-
下载生成的 ZIP 文件,它是使用您的选择配置的 Web 应用程序的存档。
如果您的 IDE 集成了 Spring Initializr,您可以通过 IDE 完成此过程。 |
您也可以从 Github 分叉项目并在您的 IDE 或其他编辑器中打开它。 |
定义模式
在此示例中,您存储 `Customer` 对象,每个对象都用作 R2DBC 实体进行注释。以下清单显示了 SQL 模式类(位于 `src/main/resources/schema.sql` 中)
这里有一个包含三列的 `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 文件,如下所示:
如果您使用 Maven,可以使用 `./mvnw spring-boot:run` 运行应用程序。或者,您可以使用 `./mvnw clean package` 构建 JAR 文件,然后运行 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 将对象保存到数据库并从中提取对象,而无需编写具体的存储库实现。