将 Spring Data R2DBC 与 Azure Database for PostgreSQL 配合使用

本文演示如何创建示例应用程序,使其使用 Spring Data R2DBCAzure Database for PostgreSQL 数据库中存储和检索信息。 该示例从 GitHub 上 r2dbc-postgresql 存储库中使用 PostgreSQL 的 R2DBC 实现。

R2DBC 将反应式 API 引入传统的关系数据库。 可以将它与 Spring WebFlux 配合使用,创建使用非阻止式 API 的完全响应式 Spring Boot 应用程序。 它提供的可伸缩性优于经典的“一个连接一个线程”方法。

先决条件

请参阅示例应用程序

在本文中,你将编写一个示例应用程序。 如果希望加快进程,可通过 https://github.com/Azure-Samples/quickstart-spring-data-r2dbc-postgresql 获得已编码的应用程序。

准备工作环境

首先,通过运行以下命令来设置一些环境变量:

export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_SERVER_NAME=<YOUR_DATABASE_SERVER_NAME>
export AZ_DATABASE_NAME=<YOUR_DATABASE_NAME>
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_POSTGRESQL_ADMIN_USERNAME=spring
export AZ_POSTGRESQL_ADMIN_PASSWORD=<YOUR_POSTGRESQL_ADMIN_PASSWORD>
export AZ_POSTGRESQL_NON_ADMIN_USERNAME=nonspring
export AZ_POSTGRESQL_NON_ADMIN_PASSWORD=<YOUR_POSTGRESQL_NON_ADMIN_PASSWORD>
export AZ_LOCAL_IP_ADDRESS=<YOUR_LOCAL_IP_ADDRESS>

使用以下值替换占位符,在本文中将使用这些值:

  • <YOUR_DATABASE_SERVER_NAME>:PostgreSQL 服务器的名称,应在整个 Azure 中保持唯一。
  • <YOUR_DATABASE_NAME>:PostgreSQL 服务器的数据库名称,该名称在 Azure 中应是唯一的。
  • <YOUR_AZURE_REGION>:将使用的 Azure 区域。 默认情况下可以使用 eastus,但我们建议你配置一个离居住位置更近的区域。 可以使用 查看可用区域 az account list-locations的完整列表。
  • <YOUR_POSTGRESQL_ADMIN_PASSWORD><YOUR_POSTGRESQL_NON_ADMIN_PASSWORD>:PostgreSQL 数据库服务器的密码,该服务器至少应包含 8 个字符。 这些字符应该属于以下类别中的三个类别:英文大写字母、英文小写字母、数字 (0-9)和非字母数字字符(!, $, #, % 等)。
  • <YOUR_LOCAL_IP_ADDRESS>:你将在其中运行 Spring Boot 应用程序的本地计算机的 IP 地址。 一种查看 IP 地址的便捷方法是打开 whatismyip.akamai.com

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

接下来,请使用以下命令创建资源组:

az group create \
    --name $AZ_RESOURCE_GROUP \
    --location $AZ_LOCATION \
    --output tsv

创建 Azure Database for PostgreSQL 实例并设置管理员用户

首先需要创建具有管理员用户的托管 PostgreSQL 服务器。

注意

可以在使用 Azure 门户创建 Azure Database for PostgreSQL 服务器中阅读有关创建 PostgreSQL 服务器的更多详细信息。

az postgres flexible-server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_SERVER_NAME \
    --location $AZ_LOCATION \
    --admin-user $AZ_POSTGRESQL_ADMIN_USERNAME \
    --admin-password $AZ_POSTGRESQL_ADMIN_PASSWORD \
    --yes \
    --output tsv

配置 PostgreSQL 数据库

你在前面创建的 PostgreSQL 服务器为空。 使用以下命令创建新数据库。

az postgres flexible-server db create \
    --resource-group $AZ_RESOURCE_GROUP \
    --database-name $AZ_DATABASE_NAME \
    --server-name $AZ_DATABASE_SERVER_NAME \
    --output tsv

为 PostgreSQL 服务器配置防火墙规则

Azure Database for PostgreSQL 实例在默认情况下受保护。 它们有不允许任何传入连接的防火墙。 为了能够使用数据库,需要添加一项防火墙规则,允许本地 IP 地址访问数据库服务器。

由于我们已在本文开头配置了本地 IP 地址,因此你可通过运行以下命令来打开服务器的防火墙:

az postgres flexible-server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_SERVER_NAME \
    --rule-name $AZ_DATABASE_SERVER_NAME-database-allow-local-ip \
    --start-ip-address $AZ_LOCAL_IP_ADDRESS \
    --end-ip-address $AZ_LOCAL_IP_ADDRESS \
    --output tsv

如果要在 Windows 计算机上从适用于 Linux 的 Windows 子系统连接到 PostgreSQL 服务器,则需要将 WSL 主机 ID 添加到防火墙。

通过在 WSL 中运行以下命令获取主机的 IP 地址:

cat /etc/resolv.conf

复制 nameserver 一词后面的 IP 地址,然后使用以下命令为 WSL IP 地址设置环境变量:

export AZ_WSL_IP_ADDRESS=<the-copied-IP-address>

然后使用以下命令向基于 WSL 的应用开放服务器的防火墙:

az postgres flexible-server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_SERVER_NAME \
    --rule-name $AZ_DATABASE_SERVER_NAME-database-allow-local-ip \
    --start-ip-address $AZ_WSL_IP_ADDRESS \
    --end-ip-address $AZ_WSL_IP_ADDRESS \
    --output tsv

创建 PostgreSQL 非管理员用户并授予权限

接下来创建一个非管理员用户,并为其授予对数据库的所有权限。

注意

可以阅读在 Azure Database for PostgreSQL 中创建用户来获取有关创建 PostgreSQL 用户的更详细信息。

创建一个名为 create_user.sql 的 SQL 脚本用于创建非管理员用户。 添加以下内容,在本地保存该脚本:

注意

Microsoft 建议使用最安全的可用身份验证流。 此过程中所述的身份验证流(例如数据库、缓存、消息传送或 AI 服务)需要高度信任应用程序,并且存在其他流中不存在的风险。 仅当更安全的选项(例如无密码连接或无密钥连接的托管标识)不可行时,才使用此流。 对于本地计算机操作,首选无密码连接或无密钥连接的用户标识。

cat << EOF > create_user.sql
CREATE ROLE "$AZ_POSTGRESQL_NON_ADMIN_USERNAME" WITH LOGIN PASSWORD '$AZ_POSTGRESQL_NON_ADMIN_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE $AZ_DATABASE_NAME TO "$AZ_POSTGRESQL_NON_ADMIN_USERNAME";
EOF

然后,使用以下命令运行该 SQL 脚本以创建 Microsoft Entra 非管理员用户:

psql "host=$AZ_DATABASE_SERVER_NAME.postgres.database.azure.com user=$AZ_POSTGRESQL_ADMIN_USERNAME dbname=$AZ_DATABASE_NAME port=5432 password=$AZ_POSTGRESQL_ADMIN_PASSWORD sslmode=require" < create_user.sql

现在使用以下命令删除临时 SQL 脚本文件:

rm create_user.sql

创建反应式 Spring Boot 应用程序

若要创建一个响应式 Spring Boot 应用程序,可使用 Spring Initializr。 要创建的应用程序使用:

  • Spring Boot 2.7.11。
  • 以下依赖项:Spring 反应 Web(也称为 Spring WebFlux)和 Spring Data R2DBC。

使用 Spring Initializr 生成应用程序

使用以下命令在命令行上生成应用程序:

curl https://start.spring.io/starter.tgz -d dependencies=webflux,data-r2dbc -d baseDir=azure-database-workshop -d bootVersion=2.7.11 -d javaVersion=17 | tar -xzvf -

添加响应式 PostgreSQL 驱动程序实现

打开所生成项目的 pom.xml 文件,然后添加来自 GitHub 上 r2dbc-postgresql 存储库的响应式 PostgreSQL 驱动程序。 在 spring-boot-starter-webflux 依赖项后,添加以下文本:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-postgresql</artifactId>
    <version>0.8.12.RELEASE</version>
    <scope>runtime</scope>
</dependency>

将 Spring Boot 配置为使用 Azure Database for PostgreSQL

打开 src/main/resources/application.properties 文件,添加以下文本:

logging.level.org.springframework.data.r2dbc=DEBUG

spring.r2dbc.url=r2dbc:pool:postgres://$AZ_DATABASE_SERVER_NAME.postgres.database.azure.com:5432/$AZ_DATABASE_NAME
spring.r2dbc.username=nonspring
spring.r2dbc.password=$AZ_POSTGRESQL_NON_ADMIN_PASSWORD
spring.r2dbc.properties.sslMode=REQUIRE

$AZ_DATABASE_SERVER_NAME变量 $AZ_DATABASE_NAME$AZ_POSTGRESQL_NON_ADMIN_PASSWORD 变量替换为本文开头配置的值。

警告

出于安全原因,Azure Database for PostgreSQL 要求使用 SSL 连接。 这就是你需要添加 spring.r2dbc.properties.sslMode=REQUIRE 配置属性的原因,否则,R2DBC PostgreSQL 驱动程序将尝试使用不安全的连接进行连接,而这将会失败。

注意

为了提高性能,spring.r2dbc.url 属性配置为通过 r2dbc-pool 使用连接池。

现在,你应能够使用提供的 Maven 包装器启动应用程序,如下所示:

./mvnw spring-boot:run

下面是首次运行的应用程序的屏幕截图:

正在运行的应用程序的屏幕截图。

创建数据库架构

在主 DemoApplication 类中使用以下代码配置新的 Spring Bean,它将创建数据库架构:

package com.example.demo;

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.data.r2dbc.connectionfactory.init.ConnectionFactoryInitializer;
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;

import io.r2dbc.spi.ConnectionFactory;

@SpringBootApplication
public class DemoApplication {

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

    @Bean
    public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
        ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
        initializer.setConnectionFactory(connectionFactory);
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator(new ClassPathResource("schema.sql"));
        initializer.setDatabasePopulator(populator);
        return initializer;
    }
}

此 Spring Bean 使用名为“schema.sql”的文件,因此请在“src/main/resources”文件夹中创建该文件并添加以下文本

DROP TABLE IF EXISTS todo;
CREATE TABLE todo (id SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);

停止正在运行的应用程序并使用以下命令重启。 现在,该应用程序将使用之前创建的 demo 数据库,并在其中创建一个 todo 表。

./mvnw spring-boot:run

下面是正在创建的数据库表的屏幕截图:

创建数据库表的屏幕截图。

编写应用程序代码

接下来添加 Java 代码,以便使用 R2DBC 在 PostgreSQL 服务器中存储和检索数据。

使用以下代码,在 DemoApplication 类旁创建一个新的 Todo Java 类:

package com.example.demo;

import org.springframework.data.annotation.Id;

public class Todo {

    public Todo() {
    }

    public Todo(String description, String details, boolean done) {
        this.description = description;
        this.details = details;
        this.done = done;
    }

    @Id
    private Long id;

    private String description;

    private String details;

    private boolean done;

    public Long getId() {
        return id;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(String details) {
        this.details = details;
    }

    public boolean isDone() {
        return done;
    }

    public void setDone(boolean done) {
        this.done = done;
    }
}

此类是映射在之前创建的 todo 表上的域模型。

若要管理该类,需要一个存储库。 使用以下代码,在同一包中定义一个新的 TodoRepository 接口:

package com.example.demo;

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

public interface TodoRepository extends ReactiveCrudRepository<Todo, Long> {
}

此存储库是 Spring Data R2DBC 管理的响应式存储库。

创建可存储和检索数据的控制器,完成该应用程序。 在同一包中实现 TodoController 类,并添加以下代码:

package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/")
public class TodoController {

    private final TodoRepository todoRepository;

    public TodoController(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    @PostMapping("/")
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Todo> createTodo(@RequestBody Todo todo) {
        return todoRepository.save(todo);
    }

    @GetMapping("/")
    public Flux<Todo> getTodos() {
        return todoRepository.findAll();
    }
}

最后,使用以下命令暂停应用程序并再次启动它:

./mvnw spring-boot:run

测试应用程序

若要测试应用程序,可使用 cURL。

首先,使用以下命令在数据库中创建一个新的待办事项:

curl --header "Content-Type: application/json" \
    --request POST \
    --data '{"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done": "true"}' \
    http://127.0.0.1:8080

此命令应返回创建的项,如下所示:

{"id":1,"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done":true}

接下来,通过以下命令使用新的 cURL 请求来检索数据:

curl http://127.0.0.1:8080

此命令将返回待办事项列表,其中包括已创建的项,如下所示:

[{"id":1,"description":"configuration","details":"congratulations, you have set up R2DBC correctly!","done":true}]

下面是这些 cURL 请求的屏幕截图:

cURL 测试的屏幕截图。

祝贺你! 你已创建了一个完全响应式 Spring Boot 应用程序,该应用程序使用 R2DBC 在 Azure Database for PostgreSQL 中存储和检索数据。

清理资源

若要清理本快速入门期间使用的所有资源,请使用以下命令删除资源组:

az group delete \
    --name $AZ_RESOURCE_GROUP \
    --yes

后续步骤

若要详细了解如何将 Spring Data 应用程序部署到 Azure Spring Apps 并使用托管标识,请参阅 教程:使用与 Azure 数据库的无密码连接将 Spring 应用程序部署到 Azure Spring Apps。

若要了解有关 Spring 和 Azure 的详细信息,请继续访问“Azure 上的 Spring”文档中心。

另请参阅

有关 Spring Data R2DBC 的详细信息,请参阅 Spring 的参考文档

若要详细了解如何将 Azure 与 Java 配合使用,请参阅面向 Java 开发人员的 Azure使用 Azure DevOps 和 Java