Usar o Spring Data R2DBC com o Banco de Dados SQL do Azure

Este artigo mostra como criar um aplicativo de exemplo que usa o Spring Data R2DBC para armazenar e recuperar informações em Banco de Dados SQL do Azure usando a implementação R2DBC para Microsoft SQL Server do repositório r2dbc-mssql GitHub.

O R2DBC traz APIs reativas para bancos de dados relacionais tradicionais. Você pode usá-lo com o Spring WebFlux para criar aplicativos Spring Boot totalmente reativos que usam APIs sem bloqueio. Ele fornece melhor escalabilidade do que a abordagem clássica "um thread por conexão".

Pré-requisitos

Consulte o aplicativo de exemplo

Neste artigo, você codifica um aplicativo de exemplo. Caso você queira adiantar o processo, esse aplicativo já está codificado e disponível em https://github.com/Azure-Samples/quickstart-spring-data-r2dbc-sql-server.

Preparar o ambiente de trabalho

Primeiro, configure algumas variáveis de ambiente usando os seguintes comandos:

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>

Substitua os espaços reservados pelos seguintes valores, que são usados ao longo deste artigo:

  • <YOUR_DATABASE_NAME>: O nome do servidor do Banco de Dados SQL do Azure, que deve ser único em todo o Azure.
  • <YOUR_AZURE_REGION>: A região do Azure que você usará. Você pode usar eastus por padrão, mas configurar uma região mais próxima de onde você mora. Consulte a lista completa de regiões disponíveis usando az account list-locations.
  • <AZ_SQL_SERVER_ADMIN_PASSWORD> e <AZ_SQL_SERVER_NON_ADMIN_PASSWORD>: A senha do servidor do Banco de Dados SQL do Azure, que deve ter no mínimo oito caracteres. Os caracteres deveriam ser de três das seguintes categorias: Letras maiúsculas, letras minúsculas, números (0-9) e caracteres não alfanuméricos (!, $, #, % e assim por diante).
  • <YOUR_LOCAL_IP_ADDRESS>: O endereço IP do computador local, do qual você executará o aplicativo Spring Boot. Uma forma conveniente de encontrá-lo é abrir whatismyip.akamai.com.

Observação

A Microsoft recomenda usar o fluxo de autenticação mais seguro disponível. O fluxo de autenticação descrito neste procedimento, como bancos de dados, caches, mensagens ou serviços de IA, requer um grau muito alto de confiança no aplicativo e traz riscos não presentes em outros fluxos. Use esse fluxo somente quando opções mais seguras, como identidades gerenciadas para conexões sem senha ou sem chave, não forem viáveis. Para operações de computador local, prefira identidades de usuário para conexões sem senha ou sem chave.

Em seguida, crie um grupo de recursos usando o seguinte comando:

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

Criar uma instância do Banco de Dados SQL do Azure

Em seguida, crie uma instância de servidor gerenciada do Banco de Dados SQL do Azure executando o comando a seguir.

Observação

A senha do MS SQL deve atender a critérios específicos e a instalação falha com uma senha não compatível. Para obter mais informações, consulte 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

Configurar uma regra de firewall para o servidor do Banco de Dados SQL do Azure

As instâncias do Banco de Dados SQL do Azure são protegidas por padrão. Elas têm um firewall que não permite nenhuma conexão de entrada. Para usar seu banco de dados, você precisa adicionar uma regra de firewall que permita que seu endereço IP local acesse o servidor de banco de dados.

Como você configurou seu endereço IP local no início deste artigo, abra o firewall do servidor executando o seguinte comando:

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

Se estiver se conectando ao servidor do Banco de Dados SQL do Azure a partir do Subsistema do Windows para Linux (WSL) em um computador Windows, será necessário adicionar o ID do host WSL ao firewall.

Obtenha o endereço IP do computador host executando o seguinte comando no WSL:

cat /etc/resolv.conf

Copie o endereço IP após o termo nameservere use o seguinte comando para definir uma variável de ambiente para o endereço IP WSL:

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

Em seguida, use o seguinte comando para abrir o firewall do servidor para seu aplicativo baseado em 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

Configurar um banco de dados SQL do Azure

O servidor do Banco de Dados SQL do Azure que você criou anteriormente está vazio. Ele não tem nenhum banco de dados que você possa usar com o aplicativo Spring Boot. Crie um banco de dados chamado demo executando o seguinte comando:

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

Criar um usuário não administrador do banco de dados SQL e conceder permissão

Nesta etapa, você cria um usuário não administrador e concede todas as permissões no demo banco de dados a esse usuário.

Crie um script SQL chamado create_user.sql para criar um usuário não administrador. Adicione o seguinte conteúdo e salve-o localmente:

Observação

A Microsoft recomenda usar o fluxo de autenticação mais seguro disponível. O fluxo de autenticação descrito neste procedimento, como bancos de dados, caches, mensagens ou serviços de IA, requer um grau muito alto de confiança no aplicativo e traz riscos não presentes em outros fluxos. Use esse fluxo somente quando opções mais seguras, como identidades gerenciadas para conexões sem senha ou sem chave, não forem viáveis. Para operações de computador local, prefira identidades de usuário para conexões sem senha ou sem chave.

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

Em seguida, use o seguinte comando para executar o script SQL e criar o usuário não administrador:

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

Observação

Para obter mais informações sobre a criação de usuários de bancos de dados SQL, consulte CREATE USER (Transact-SQL).


Criar um aplicativo Spring Boot reativo

Para criar um aplicativo Spring Boot reativo, usaremos o Spring Initializr. O aplicativo que criaremos usa:

  • Spring Boot 2.7.11.
  • As seguintes dependências: Spring Reactive Web (também conhecido como Spring WebFlux) e Spring Data R2DBC.

Gerar o aplicativo usando o Spring Initializr

Gere o aplicativo na linha de comando executando o seguinte comando:

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 -

Adicionar a implementação reativa do driver do Banco de Dados SQL do Azure

Abra o arquivo pom.xml do projeto gerado para adicionar o driver reativo do Banco de Dados SQL do Azure do repositório do GitHub r2dbc-mssql.

Após a dependência spring-boot-starter-webflux, adicione o seguinte texto:

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

Configurar o Spring Boot para usar o Banco de Dados SQL do Azure

Abra o arquivo src/main/resources/application.properties e adicione o seguinte texto:

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

Substitua as duas variáveis $AZ_DATABASE_NAME e a variável $AZ_SQL_SERVER_NON_ADMIN_PASSWORD pelos valores que você configurou no início deste artigo.

Observação

Para obter um melhor desempenho, a propriedade spring.r2dbc.url está configurada para usar um pool de conexões usando r2dbc-pool.

Agora você pode iniciar seu aplicativo usando o wrapper do Maven fornecido da seguinte maneira:

./mvnw spring-boot:run

Aqui está uma captura de tela do aplicativo em execução pela primeira vez:

Captura de tela do aplicativo em execução.

Criar o esquema de banco de dados

Dentro da classe principal DemoApplication, configure um novo bean do Spring que criará um esquema de banco de dados, usando o seguinte código:

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;
    }
}

Esse bean do Spring usa um arquivo chamado schema.sql; portanto, crie esse arquivo na pasta src/main/resources e adicione o seguinte texto:

DROP TABLE IF EXISTS todo;
CREATE TABLE todo (id INT IDENTITY PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BIT);

Pare o aplicativo em execução e inicie-o novamente usando o comando a seguir. O aplicativo agora usa o demo banco de dados que você criou anteriormente e cria uma todo tabela dentro dele.

./mvnw spring-boot:run

Aqui está uma captura de tela da tabela do banco de dados enquanto está sendo criada:

Captura de tela da criação da tabela do banco de dados.

Codificar o aplicativo

Em seguida, adicione o código Java que usa R2DBC para armazenar e recuperar dados do servidor Banco de Dados SQL do Azure.

Crie uma classe Java Todo ao lado da classe DemoApplication usando o seguinte código:

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;
    }
}

Essa classe é um modelo de domínio mapeado na tabela de todo que você criou anteriormente.

Para gerenciar essa classe, você precisará de um repositório. Defina uma nova interface TodoRepository no mesmo pacote usando o seguinte código:

package com.example.demo;

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

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

Esse repositório é um repositório reativo que o Spring Data R2DBC gerencia.

Conclua o aplicativo criando um controlador que pode armazenar e recuperar dados. Implemente uma classe TodoController no mesmo pacote e adicione o seguinte código:

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();
    }
}

Por fim, pare o aplicativo e inicie-o novamente usando o seguinte comando:

./mvnw spring-boot:run

Testar o aplicativo

Para testar o aplicativo, você pode usar cURL.

Primeiro, crie um item "todo" no banco de dados com o seguinte comando:

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

Este comando deverá retornar o item criado, conforme mostrado aqui:

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

Em seguida, recupere os dados usando uma nova solicitação do cURL com o seguinte comando:

curl http://127.0.0.1:8080

Esse comando retorna a lista de itens "todo", incluindo o item que você criou, conforme mostrado aqui:

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

Aqui está uma captura de tela dessas solicitações cURL:

Captura de tela do teste do cURL.

Parabéns! Você criou um aplicativo Spring Boot totalmente reativo que usa R2DBC para armazenar e recuperar dados de Banco de Dados SQL do Azure.

Limpar os recursos

Para limpar todos os recursos usados durante este início rápido, exclua o grupo de recursos usando o seguinte comando:

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

Próximas etapas

Para saber mais sobre como implantar um aplicativo Spring Data no Aplicativos Spring do Azure e usar a identidade gerenciada, consulte Tutorial: Implantar um aplicativo Spring no Aplicativos Spring do Azure com uma conexão sem senha com um banco de dados do Azure.

Para saber mais sobre o Spring e o Azure, continue no Spring no Centro de Documentação do Azure.

Confira também

Para obter mais informações sobre o Spring Data R2DBC, confira a documentação de referência do Spring.

Para obter mais informações sobre como usar o Azure com o Java, confira Azure para desenvolvedores Java e Trabalhar com o Azure DevOps e com o Java.