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

本文演示如何通过使用 r2dbc-mysql GitHub 存储库中 MySQL 的 R2DBC 实现创建一个示例应用程序,该应用程序使用 Spring Data R2DBCAzure Database for MySQL 中存储和检索信息。

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

先决条件

请参阅示例应用程序

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

准备工作环境

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

export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_NAME=<YOUR_DATABASE_NAME>
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_MYSQL_ADMIN_USERNAME=spring
export AZ_MYSQL_ADMIN_PASSWORD=<YOUR_MYSQL_ADMIN_PASSWORD>
export AZ_MYSQL_NON_ADMIN_USERNAME=spring-non-admin
export AZ_MYSQL_NON_ADMIN_PASSWORD=<YOUR_MYSQL_NON_ADMIN_PASSWORD>

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

  • <YOUR_DATABASE_NAME>:MySQL 服务器的名称,该名称应在整个 Azure 中独一无二。
  • <YOUR_AZURE_REGION>:将使用的 Azure 区域。 默认情况下可以使用 eastus,但我们建议你配置一个离居住位置更近的区域。 可以使用 查看可用区域 az account list-locations的完整列表。
  • <YOUR_MYSQL_ADMIN_PASSWORD><YOUR_MYSQL_NON_ADMIN_PASSWORD>:MySQL 数据库服务器的密码,至少应包含 8 个字符。 这些字符应该属于以下类别中的三个类别:英文大写字母、英文小写字母、数字 (0-9)和非字母数字字符(!, $, #, % 等)。

接下来,创建一个资源组:

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

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

首先,你将创建一个托管 MySQL 服务器,其中包含管理员用户。

注意

可以参阅使用 Azure 门户创建 Azure Database for MySQL 服务器,详细了解如何创建 MySQL 服务器。

az mysql flexible-server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME \
    --location $AZ_LOCATION \
    --admin-user $AZ_MYSQL_ADMIN_USERNAME \
    --admin-password $AZ_MYSQL_ADMIN_PASSWORD \
    --yes \
    --output tsv

配置 MySQL 数据库

使用以下命令创建名为 demo 的新数据库:

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

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

Azure Database for MySQL 实例在默认情况下受保护。 它们有不允许任何传入连接的防火墙。

如果你使用的是 Bash,则可以跳过此步骤,因为 flexible-server create 命令已检测到你的本地 IP 地址并在 MySQL 服务器上设置了该地址。

如果要从 Windows 计算机上的 适用于 Linux 的 Windows 子系统 (WSL) 连接到 MySQL 服务器,则需要将 WSL 主机 ID 添加到防火墙。 通过在 WSL 中运行以下命令获取主机的 IP 地址:

cat /etc/resolv.conf

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

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

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

az mysql flexible-server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME \
    --start-ip-address $AZ_WSL_IP_ADDRESS \
    --end-ip-address $AZ_WSL_IP_ADDRESS \
    --rule-name allowiprange \
    --output tsv

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

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

注意

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

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

cat << EOF > create_user.sql
CREATE USER '$AZ_MYSQL_NON_ADMIN_USERNAME'@'%' IDENTIFIED BY '$AZ_MYSQL_NON_ADMIN_PASSWORD';
GRANT ALL PRIVILEGES ON demo.* TO '$AZ_MYSQL_NON_ADMIN_USERNAME'@'%';
FLUSH PRIVILEGES;
EOF

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

mysql -h $AZ_DATABASE_NAME.mysql.database.azure.com --user $AZ_MYSQL_ADMIN_USERNAME --enable-cleartext-plugin --password=$AZ_MYSQL_ADMIN_PASSWORD < 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 -

添加反应式 MySQL 驱动程序实现

打开所生成项目的 pom.xml 文件,添加来自 GitHub 上的 r2dbc-mysql 存储库的响应式 MySQL 驱动程序。

spring-boot-starter-webflux 依赖项后,添加以下代码片段:

<dependency>
  <groupId>io.asyncer</groupId>
  <artifactId>r2dbc-mysql</artifactId>
  <version>0.9.1</version>
</dependency>

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

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

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

spring.r2dbc.url=r2dbc:pool:mysql://$AZ_DATABASE_NAME.mysql.database.azure.com:3306/demo?tlsVersion=TLSv1.2
spring.r2dbc.username=spring-non-admin
spring.r2dbc.password=$AZ_MYSQL_NON_ADMIN_PASSWORD

$AZ_DATABASE_NAME 变量 $AZ_MYSQL_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 SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);

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

./mvnw spring-boot:run

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

Screenshot of the creation of the database table.

编写应用程序代码

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

使用以下代码,在 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 Database for MySQL 中存储和检索数据。

清理资源

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

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