Usare Spring Data R2DBC con Database di Azure per PostgreSQL

Questo articolo illustra la creazione di un'applicazione di esempio che usa Spring Data R2DBC per archiviare e recuperare informazioni in un database Database di Azure per PostgreSQL. L'esempio usa l'implementazione di R2DBC per PostgreSQL dal repository r2dbc-postgresql in GitHub.

R2DBC introduce le API reattive nei database relazionali tradizionali. È possibile usarlo con Spring WebFlux per creare applicazioni Spring Boot completamente reattive che usano API non bloccanti. Offre una migliore scalabilità rispetto all'approccio classico di "un thread per connessione".

Prerequisiti

  • Client della riga di comando postgreSQL.

  • cURL o un'utilità HTTP simile per testare la funzionalità.

Vedere l'applicazione di esempio

In questo articolo verrà codificata un'applicazione di esempio. Per procedere più rapidamente, è possibile usare l'applicazione già pronta, disponibile all'indirizzo https://github.com/Azure-Samples/quickstart-spring-data-r2dbc-postgresql.

Preparare l'ambiente di lavoro

Prima di tutto, configurare alcune variabili di ambiente eseguendo i comandi seguenti:

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>

Sostituire i segnaposto con i valori seguenti, che vengono usati nell'intero articolo:

  • <YOUR_DATABASE_SERVER_NAME>: nome del server PostgreSQL, che deve essere univoco in Azure.
  • <YOUR_DATABASE_NAME>: nome del database del server PostgreSQL, che deve essere univoco all'interno di Azure.
  • <YOUR_AZURE_REGION>: l'area di Azure che verrà usata. È possibile usare eastus per impostazione predefinita, ma è consigliabile configurare un'area più vicina a dove si risiede. È possibile visualizzare l'elenco completo delle aree disponibili usando az account list-locations.
  • <YOUR_POSTGRESQL_ADMIN_PASSWORD> e <YOUR_POSTGRESQL_NON_ADMIN_PASSWORD>: password del server di database PostgreSQL, che deve contenere almeno otto caratteri. I caratteri devono essere compresi tra tre delle categorie seguenti: lettere maiuscole, lettere minuscole, numeri (0-9) e caratteri non alfanumerici (!, $, #, %e così via).
  • <YOUR_LOCAL_IP_ADDRESS>: indirizzo IP del computer locale, da cui si eseguirà l'applicazione Spring Boot. Un modo pratico per trovare è aprire whatismyip.akamai.com.

Creare quindi un gruppo di risorse usando il comando seguente:

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

Creare un'istanza di Database di Azure per PostgreSQL e configurare l'utente amministratore

La prima cosa che si creerà è un server PostgreSQL gestito con un utente amministratore.

Nota

Per informazioni più dettagliate sulla creazione di server PostgreSQL, vedere Creare un server di Database di Azure per PostgreSQL nel portale di Azure.

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

Configurare un database PostgreSQL

Il server PostgreSQL creato in precedenza è vuoto. Usare il comando seguente per creare un nuovo database.

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

Configurare una regola del firewall per il server PostgreSQL

Le istanze di Database di Azure per PostgreSQL sono protette per impostazione predefinita. Includono un firewall che non consente alcuna connessione in ingresso. Per poter usare il database, è necessario aggiungere una regola del firewall che consenta all'indirizzo IP locale di accedere al server di database.

Poiché all'inizio di questo articolo è stato configurato un indirizzo IP locale, è possibile aprire il firewall del server eseguendo questo comando:

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

Se ci si connette al server PostgreSQL da sottosistema Windows per Linux (WSL) in un computer Windows, è necessario aggiungere l'ID host WSL al firewall.

Ottenere l'indirizzo IP del computer host eseguendo il comando seguente in WSL:

cat /etc/resolv.conf

Copiare l'indirizzo IP seguendo il termine nameserver, quindi usare il comando seguente per impostare una variabile di ambiente per l'indirizzo IP WSL:

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

Usare quindi il comando seguente per aprire il firewall del server all'app basata su 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

Creare un utente non amministratore di PostgreSQL e concedere l'autorizzazione

Creare quindi un utente non amministratore e concedere tutte le autorizzazioni al database.

Nota

Per altre informazioni dettagliate sulla creazione di utenti PostgreSQL, vedere Creare utenti in Database di Azure per PostgreSQL.

Creare uno script SQL denominato create_user.sql per la creazione di un utente non amministratore. Aggiungere il contenuto seguente e salvarlo in locale:

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

Usare quindi il comando seguente per eseguire lo script SQL per creare l'utente non amministratore di 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

Usare ora il comando seguente per rimuovere il file di script SQL temporaneo:

rm create_user.sql

Creare un'applicazione Spring Boot reattiva

Per creare un'applicazione Spring Boot reattiva, si userà Spring Initializr. L'applicazione che verrà creata usa:

  • Spring Boot 2.7.11.
  • Le dipendenze seguenti: Spring Reactive Web (nota anche come Spring WebFlux) e Spring Data R2DBC.

Generare l'applicazione con Spring Initializr

Per generare l'applicazione, immettere il comando seguente sulla riga di 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 -

Aggiungere l'implementazione del driver PostgreSQL reattivo

Aprire il file pom.xml del progetto generato, quindi aggiungere il driver PostgreSQL reattivo dal repository r2dbc-postgresql in GitHub. Dopo la dipendenza spring-boot-starter-webflux aggiungere il testo seguente:

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

Configurare Spring Boot per l'uso di Database di Azure per PostgreSQL

Aprire il file src/main/resources/application.properties e aggiungere il testo seguente:

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

Sostituire le $AZ_DATABASE_SERVER_NAMEvariabili , $AZ_DATABASE_NAMEe $AZ_POSTGRESQL_NON_ADMIN_PASSWORD con i valori configurati all'inizio di questo articolo.

Avviso

Per motivi di sicurezza, con Database di Azure per PostgreSQL è necessario usare connessioni SSL. Questo è il motivo per cui è necessario aggiungere la proprietà di configurazione spring.r2dbc.properties.sslMode=REQUIRE. In caso contrario, il driver PostgreSQL R2DBC proverà, senza riuscirci, a connettersi usando una connessione non sicura.

Nota

Per prestazioni più elevate, la proprietà spring.r2dbc.url viene configurata per l'uso di un pool di connessioni tramite r2dbc-pool.

A questo punto dovrebbe essere possibile avviare l'applicazione usando il wrapper Maven fornito, come segue:

./mvnw spring-boot:run

Ecco uno screenshot dell'applicazione in esecuzione per la prima volta:

Screenshot of the running application.

Creare lo schema del database

All'interno della classe DemoApplication principale configurare un nuovo bean Spring che creerà uno schema del database, usando il codice seguente:

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

Questo bean Spring usa un file denominato schema.sql, quindi creare il file nella cartella src/main/resources e aggiungere il testo seguente:

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

Arrestare l'applicazione in esecuzione e quindi riavviarla usando il comando seguente. L'applicazione userà ora il database demo creato in precedenza e creerà una todo tabella al suo interno.

./mvnw spring-boot:run

Ecco uno screenshot della tabella di database creata:

Screenshot of the creation of the database table.

Codice dell'applicazione

Aggiungere quindi il codice Java che userà R2DBC per archiviare e recuperare i dati dal server PostgreSQL.

Creare una nuova classe Java Todo accanto alla classe DemoApplication usando il codice seguente:

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

Questa classe è un modello di dominio mappato alla tabella todo creata in precedenza.

Per gestire questa classe è necessario un repository. Definire una nuova interfaccia TodoRepository nello stesso pacchetto usando il codice seguente:

package com.example.demo;

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

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

Questo repository è un repository reattivo gestito da Spring Data R2DBC.

Completare l'applicazione creando un controller in grado di archiviare e recuperare dati. Implementare una classe TodoController nello stesso pacchetto e aggiungere il codice seguente:

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

Infine, arrestare l'applicazione e quindi riavviarla usando il comando seguente:

./mvnw spring-boot:run

Testare l'applicazione

Per testare l'applicazione, è possibile usare cURL.

Creare prima di tutto un elemento "todo" nel database con il comando seguente:

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

Questo comando restituirà l'elemento creato, come illustrato qui:

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

Recuperare quindi i dati usando una nuova richiesta cURL con il comando seguente:

curl http://127.0.0.1:8080

Questo comando restituirà l'elenco di elementi "todo", incluso quello creato, come illustrato qui:

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

Ecco uno screenshot di queste richieste cURL:

Screenshot of the cURL test.

Complimenti. È stata creata un'applicazione Spring Boot completamente reattiva che usa R2DBC per archiviare e recuperare i dati da Database di Azure per PostgreSQL.

Pulire le risorse

Per pulire tutte le risorse usate durante questa guida introduttiva, eliminare il gruppo di risorse usando il comando seguente:

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

Passaggi successivi

Per altre informazioni sulla distribuzione di un'applicazione Spring Data in Azure Spring Apps e sull'uso dell'identità gestita, vedere Esercitazione: Distribuire un'applicazione Spring in Azure Spring Apps con una connessione senza password a un database di Azure.

Per altre informazioni su Spring e Azure, passare al centro di documentazione di Spring in Azure.

Vedi anche

Per altre informazioni su Spring Data R2DBC, vedere la documentazione di riferimento di Spring.

Per altre informazioni sull'uso di Azure con Java, vedere Azure per sviluppatori Java e la documentazione relativa all'uso di Azure DevOps e Java.