将 Spring Data R2DBC 用于 Azure SQL 数据库

本文演示如何通过使用 r2dbc-mssql GitHub 存储库中适用于 Microsoft SQL Server 的 R2DBC 实现创建一个示例应用程序,该应用程序使用 Spring Data R2DBCAzure SQL 数据库中存储和检索信息。

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

先决条件

请参阅示例应用程序

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

准备工作环境

首先,使用以下命令设置一些环境变量:

export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_NAME=<YOUR_DATABASE_NAME>
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_SQL_SERVER_ADMIN_USERNAME=spring
export AZ_SQL_SERVER_ADMIN_PASSWORD=<YOUR_AZURE_SQL_ADMIN_PASSWORD>
export AZ_SQL_SERVER_NON_ADMIN_USERNAME=nonspring
export AZ_SQL_SERVER_NON_ADMIN_PASSWORD=<YOUR_AZURE_SQL_NON_ADMIN_PASSWORD>
export AZ_LOCAL_IP_ADDRESS=<YOUR_LOCAL_IP_ADDRESS>

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

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

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

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

创建 Azure SQL 数据库实例

接下来,运行以下命令创建托管Azure SQL 数据库服务器实例。

注意

MS SQL 密码必须满足特定条件,并且安装程序会失败并显示不符合密码。 有关详细信息,请参阅 Password Policy

az sql server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME \
    --location $AZ_LOCATION \
    --admin-user $AZ_SQL_SERVER_ADMIN_USERNAME \
    --admin-password $AZ_SQL_SERVER_ADMIN_PASSWORD \
    --output tsv

为 Azure SQL 数据库服务器配置防火墙规则

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

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

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

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

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

cat /etc/resolv.conf

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

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

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


az sql server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME-database-allow-local-ip-wsl \
    --server $AZ_DATABASE_NAME \
    --start-ip-address $AZ_WSL_IP_ADDRESS \
    --end-ip-address $AZ_WSL_IP_ADDRESS \
    --output tsv

配置 Azure SQL 数据库

你在前面创建的 Azure SQL 数据库服务器为空。 它没有任何可以与 Spring Boot 应用程序配合使用的数据库。 通过运行以下命令创建名为 demo 的新数据库:

az sql db create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name demo \
    --server $AZ_DATABASE_NAME \
    --output tsv

创建 SQL 数据库非管理员用户并授予权限

此步骤将创建非管理员用户,并向该用户授予对数据库的所有权限 demo

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

cat << EOF > create_user.sql
USE demo;
GO
CREATE USER $AZ_SQL_SERVER_NON_ADMIN_USERNAME WITH PASSWORD='$AZ_SQL_SERVER_NON_ADMIN_PASSWORD'
GO
GRANT CONTROL ON DATABASE::demo TO $AZ_SQL_SERVER_NON_ADMIN_USERNAME;
GO
EOF

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

sqlcmd -S $AZ_DATABASE_NAME.database.windows.net,1433  -d demo -U $AZ_SQL_SERVER_ADMIN_USERNAME -P $AZ_SQL_SERVER_ADMIN_PASSWORD  -i create_user.sql

注意

有关创建 SQL 数据库用户的详细信息,请参阅 CREATE U标准版R (Transact-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 -

添加响应式 Azure SQL 数据库驱动程序实现

打开所生成项目的 pom.xml 文件,以添加来自 r2dbc-mssql GitHub 存储库的响应式 Azure SQL 数据库驱动程序。

spring-boot-starter-webflux 依赖项后,添加以下文本:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-mssql</artifactId>
    <scope>runtime</scope>
</dependency>

将 Spring Boot 配置为使用 Azure SQL 数据库

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

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

spring.r2dbc.url=r2dbc:pool:mssql://$AZ_DATABASE_NAME.database.windows.net:1433/demo
spring.r2dbc.username=nonspring@$AZ_DATABASE_NAME
spring.r2dbc.password=$AZ_SQL_SERVER_NON_ADMIN_PASSWORD

将这两个 $AZ_DATABASE_NAME 变量和 $AZ_SQL_SERVER_NON_ADMIN_PASSWORD 变量替换为在本文开头部分配置的值。

注意

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

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

./mvnw spring-boot:run

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

Screenshot of the running application.

创建数据库架构

在主 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 INT IDENTITY PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BIT);

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

./mvnw spring-boot:run

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

Screenshot of the creation of the database table.

编写应用程序代码

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

使用以下代码,在 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 请求的屏幕截图:

Screenshot of the cURL test.

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

清理资源

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

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