Início rápido: Utilize Java e JDBC para o Azure HorizonDB (Pré-visualização)

Este artigo demonstra como criar uma aplicação de exemplo que utiliza Java e JDBC para armazenar e recuperar informação em Azure HorizonDB (Preview).

JDBC é a API padrão Java para ligação a bases de dados relacionais tradicionais.

Os passos deste artigo incluem autenticação PostgreSQL.

A autenticação PostgreSQL usa contas armazenadas no PostgreSQL, e tens de gerir tu próprio a rotação das palavras-passe.

Pré-requisitos

Preparar o ambiente de trabalho

Primeiro, use o seguinte comando para configurar algumas variáveis de ambiente.

export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_CLUSTER_NAME=<YOUR_DATABASE_CLUSTER_NAME>
export AZ_DATABASE_NAME=<YOUR_DATABASE_NAME>
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_POSTGRESQL_ADMIN_USERNAME=demo
export AZ_POSTGRESQL_ADMIN_PASSWORD=<YOUR_POSTGRESQL_ADMIN_PASSWORD>
export AZ_POSTGRESQL_NON_ADMIN_USERNAME=demo-non-admin
export AZ_POSTGRESQL_NON_ADMIN_PASSWORD=<YOUR_POSTGRESQL_NON_ADMIN_PASSWORD>
export AZ_LOCAL_IP_ADDRESS=<YOUR_LOCAL_IP_ADDRESS>

Substitua os placeholders pelos seguintes valores, que são usados ao longo deste artigo.

  • <YOUR_DATABASE_CLUSTER_NAME>: O nome do seu cluster do Azure HorizonDB, que deve ser único na sua subscrição e no seu grupo de recursos do Azure.
  • <YOUR_DATABASE_NAME>: O nome da base de dados que está a usar dentro do seu cluster Azure HorizonDB.
  • <YOUR_AZURE_REGION>: A região do Azure a ser usada. Podes usar australiaeast por defeito, mas configura uma região mais próxima de onde vives. Você pode ver a lista completa de regiões disponíveis digitando az account list-locations.
  • <YOUR_POSTGRESQL_ADMIN_PASSWORD> e <YOUR_POSTGRESQL_NON_ADMIN_PASSWORD>: A palavra-passe do seu cluster Azure HorizonDB. Essa palavra-passe deve ter pelo menos oito carateres. Os caracteres devem ser de três das seguintes categorias: letras maiúsculas inglesas, letras minúsculas inglesas, números (0-9) e caracteres não alfanuméricos (!, $, #, % e assim por diante).
  • <YOUR_LOCAL_IP_ADDRESS>: O endereço IP do seu computador local, a partir do qual executa a sua aplicação Spring Boot. Uma forma conveniente de a encontrar é abrir whatismyip.akamai.com.

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

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

Criar um cluster Azure HorizonDB

Note

Não inclua qualquer informação pessoal, sensível ou confidencial nos nomes dos recursos (por exemplo, nome da tabela, nome da base de dados) e nas etiquetas de recursos. Os dados que insere nestes campos não são considerados dados do cliente.

As secções seguintes descrevem como criar e configurar o seu cluster de base de dados.

Criar um cluster Azure HorizonDB

Configurar utilizador de administrador

Primeiro, crie uma instância Azure HorizonDB gerida.

Note

Para informações mais detalhadas sobre a criação de Azure HorizonDB, veja Criar um cluster Azure HorizonDB.

az horizondb create \
  --resource-group $AZ_RESOURCE_GROUP \
  --name $AZ_DATABASE_CLUSTER_NAME \
  --location $AZ_LOCATION \
  --version 17 \
  --administrator-login $AZ_POSTGRESQL_ADMIN_USERNAME \
  --administrator-login-password $AZ_POSTGRESQL_ADMIN_PASSWORD \
  --v-cores 2 \
  --yes \
  --output tsv

Este comando cria um pequeno cluster Azure HorizonDB.

Tem algum problema? Deixe-nos saber.

Configure uma regra de firewall para a sua instância do Azure HorizonDB

As instâncias do Azure HorizonDB são seguras por defeito. Eles têm um firewall que bloqueia todas as ligações de entrada. Para usar a sua base de dados, adicione uma regra de firewall que conceda ao seu endereço IP local acesso ao servidor da base de dados.

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

az horizondb firewall-rule create \
  --resource-group $AZ_RESOURCE_GROUP \
  --cluster-name $AZ_DATABASE_CLUSTER_NAME \
  --firewall-rule-name $AZ_DATABASE_CLUSTER_NAME-database-allow-local-ip \
  --start-ip-address $AZ_LOCAL_IP_ADDRESS \
  --end-ip-address $AZ_LOCAL_IP_ADDRESS \
  --output tsv

Se estiver a ligar-se ao seu cluster Azure HorizonDB a partir do Subsistema Windows para Linux (WSL) num computador Windows, precisa de adicionar o ID do host WSL ao seu firewall.

Obtenha o endereço IP da sua máquina anfitriã executando o seguinte comando em WSL:

cat /etc/resolv.conf

Copie o endereço IP que se segue ao termo nameserver, e depois use o seguinte comando para definir uma variável de ambiente para o endereço IP WSL:

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 horizondb firewall-rule create \
  --resource-group $AZ_RESOURCE_GROUP \
  --cluster-name $AZ_DATABASE_CLUSTER_NAME \
  --firewall-rule-name $AZ_DATABASE_CLUSTER_NAME-database-allow-local-ip \
  --start-ip-address $AZ_WSL_IP_ADDRESS \
  --end-ip-address $AZ_WSL_IP_ADDRESS \
  --output tsv

Criar uma base de dados Azure HorizonDB

Cria um script SQL chamado create_database.sql para criar uma nova base de dados no teu cluster. Adicione o seguinte conteúdo e guarde o ficheiro localmente:

cat << EOF > create_database.sql
CREATE DATABASE "$AZ_DATABASE_NAME";
EOF

Execute este comando para obter o seu domínio totalmente qualificado do HorizonDB Azure e copie o valor clusterName:

az horizondb show \
  --resource-group $AZ_RESOURCE_GROUP \
  --name $AZ_DATABASE_CLUSTER_NAME \
  --query "{clusterName:properties.fullyQualifiedDomainName, adminUser:properties.administratorLogin}" \
  --output table

Copie o clusterName valor e use-o no seguinte comando:

AZ_HORIZONDB_FQDN=<the-copied-clusterName-value>

Depois, execute o seguinte comando para executar o script SQL e criar a sua base de dados:

psql "host=$AZ_HORIZONDB_FQDN user=$AZ_POSTGRESQL_ADMIN_USERNAME dbname=$AZ_DATABASE_NAME port=5432 password=$AZ_POSTGRESQL_ADMIN_PASSWORD sslmode=require" < create_database.sql

Agora, execute o seguinte comando para eliminar o ficheiro de script SQL temporário:

rm create_database.sql

Crie um utilizador não administrador do Azure HorizonDB e conceda permissões

De seguida, cria um utilizador não administrador e concede todas as permissões à base de dados.

Cria um script SQL chamado create_user.sql para criar um utilizador não administrador. Adicione o seguinte conteúdo e guarde o ficheiro localmente:

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

Depois, execute o seguinte comando para executar o script SQL e criar o utilizador não administrador do Microsoft Entra:

psql "host=$AZ_HORIZONDB_FQDN user=$AZ_POSTGRESQL_ADMIN_USERNAME dbname=$AZ_DATABASE_NAME port=5432 password=$AZ_POSTGRESQL_ADMIN_PASSWORD sslmode=require" < create_user.sql

Agora, execute o seguinte comando para eliminar o ficheiro de script SQL temporário:

rm create_user.sql

Criar um novo projeto Java

Usando o seu IDE favorito, crie um novo projeto Java usando Java 8 ou posterior. Adicione um ficheiropom.xml na pasta raiz com o seguinte conteúdo:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
      <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.3.6</version>
      </dependency>
    </dependencies>
</project>

Este arquivo é um arquivo Apache Maven que configura seu projeto para usar:

  • Java 8
  • Um driver PostgreSQL recente para Java

Preparar um ficheiro de configuração para ligar ao Azure HorizonDB

Crie um ficheiro src/main/resources/application.properties e adicione o seguinte conteúdo:

cat << EOF > src/main/resources/application.properties
url=jdbc:postgresql://${AZ_HORIZONDB_FQDN}:5432/${AZ_DATABASE_NAME}?sslmode=require
user=${AZ_POSTGRESQL_NON_ADMIN_USERNAME}
password=${AZ_POSTGRESQL_NON_ADMIN_PASSWORD}
EOF

Note

A propriedade de configuração url inclui ?sslmode=require para garantir que o driver JDBC use TLS (Transport Layer Security) ao conectar-se à base de dados. O Azure HorizonDB requer TLS, e é uma prática de segurança recomendada.

Criar um arquivo SQL para gerar o esquema de banco de dados

Usa um ficheiro src/main/resources/schema.sql para criar um esquema de base de dados. Crie esse ficheiro com o seguinte conteúdo:

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

Codificar a aplicação

Conectar-se ao banco de dados

De seguida, adicione o código Java que usa JDBC para armazenar e recuperar dados da sua instância do Azure HorizonDB.

Crie um arquivo src/main/java/DemoApplication.java e adicione o seguinte conteúdo:

package com.example.demo;

import java.sql.*;
import java.util.*;
import java.util.logging.Logger;

public class DemoApplication {

    private static final Logger log;

    static {
        System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$-7s] %5$s %n");
        log =Logger.getLogger(DemoApplication.class.getName());
    }

    public static void main(String[] args) throws Exception {
        log.info("Loading application properties");
        Properties properties = new Properties();
        properties.load(DemoApplication.class.getClassLoader().getResourceAsStream("application.properties"));

        log.info("Connecting to the database");
        Connection connection = DriverManager.getConnection(properties.getProperty("url"), properties);
        log.info("Database connection test: " + connection.getCatalog());

        log.info("Create database schema");
        Scanner scanner = new Scanner(DemoApplication.class.getClassLoader().getResourceAsStream("schema.sql"));
        Statement statement = connection.createStatement();
        while (scanner.hasNextLine()) {
            statement.execute(scanner.nextLine());
        }

        /*
        Todo todo = new Todo(1L, "configuration", "congratulations, you have set up JDBC correctly!", true);
        insertData(todo, connection);
        todo = readData(connection);
        todo.setDetails("congratulations, you have updated data!");
        updateData(todo, connection);
        deleteData(todo, connection);
        */

        log.info("Closing database connection");
        connection.close();
    }
}

Tem algum problema? Deixe-nos saber.

Este código Java utiliza os ficheiros application.properties e os ficheiros schema.sql que criámos anteriormente, para se ligar à instância Azure HorizonDB e criar um esquema que armazena os nossos dados.

Neste ficheiro, pode ver que comentámos métodos para inserir, ler, atualizar e eliminar dados: vamos codificar esses métodos no resto deste artigo, e poderá retirá-los dos comentários um após o outro.

Note

As credenciais do banco de dados são armazenadas nas propriedades de usuário e senha do arquivo application.properties . Essas credenciais são usadas quando DriverManager.getConnection(properties.getProperty("url"), properties); é executado, pois o ficheiro de propriedades é passado como um argumento.

Agora você pode executar esta classe principal com sua ferramenta favorita:

  • Usando seu IDE, você deve ser capaz de clicar com o botão direito do mouse na classe DemoApplication e executá-la.
  • Usando o Maven, você pode executar o aplicativo executando: mvn exec:java -Dexec.mainClass="com.example.demo.DemoApplication".

A aplicação deve ligar-se à instância do Azure HorizonDB, criar um esquema de base de dados e depois fechar a ligação, como deve ver nos registos da consola:

[INFO   ] Loading application properties
[INFO   ] Connecting to the database
[INFO   ] Database connection test: demo
[INFO   ] Create database schema
[INFO   ] Closing database connection

Criar uma classe de domínio

Crie uma nova Todo classe Java, ao lado da DemoApplication classe, e adicione o seguinte código:

package com.example.demo;

public class Todo {

    private Long id;
    private String description;
    private String details;
    private boolean done;

    public Todo() {
    }

    public Todo(Long id, String description, String details, boolean done) {
        this.id = id;
        this.description = description;
        this.details = details;
        this.done = 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;
    }

    @Override
    public String toString() {
        return "Todo{" +
                "id=" + id +
                ", description='" + description + '\'' +
                ", details='" + details + '\'' +
                ", done=" + done +
                '}';
    }
}

Essa classe é um modelo de domínio mapeado todo na tabela que você criou ao executar o script schema.sql .

Inserir dados no Azure HorizonDB

No arquivo src/main/java/DemoApplication.java, após o método principal, adicione o seguinte método para inserir dados no banco de dados:

private static void insertData(Todo todo, Connection connection) throws SQLException {
    log.info("Insert data");
    PreparedStatement insertStatement = connection
            .prepareStatement("INSERT INTO todo (id, description, details, done) VALUES (?, ?, ?, ?);");

    insertStatement.setLong(1, todo.getId());
    insertStatement.setString(2, todo.getDescription());
    insertStatement.setString(3, todo.getDetails());
    insertStatement.setBoolean(4, todo.isDone());
    insertStatement.executeUpdate();
}

Agora pode descomentar as duas linhas seguintes no método main.

Todo todo = new Todo(1L, "configuration", "congratulations, you have set up JDBC correctly!", true);
insertData(todo, connection);

A execução da classe principal agora deve produzir a seguinte saída:

[INFO   ] Loading application properties
[INFO   ] Connecting to the database
[INFO   ] Database connection test: demo
[INFO   ] Create database schema
[INFO   ] Insert data
[INFO   ] Closing database connection

Ler dados do Azure HorizonDB

Vamos ler os dados inseridos anteriormente, para validar que o nosso código funciona corretamente.

No arquivo src/main/java/DemoApplication.java, após o insertData método, adicione o seguinte método para ler dados do banco de dados:

private static Todo readData(Connection connection) throws SQLException {
    log.info("Read data");
    PreparedStatement readStatement = connection.prepareStatement("SELECT * FROM todo;");
    ResultSet resultSet = readStatement.executeQuery();
    if (!resultSet.next()) {
        log.info("There is no data in the database!");
        return null;
    }
    Todo todo = new Todo();
    todo.setId(resultSet.getLong("id"));
    todo.setDescription(resultSet.getString("description"));
    todo.setDetails(resultSet.getString("details"));
    todo.setDone(resultSet.getBoolean("done"));
    log.info("Data read from the database: " + todo.toString());
    return todo;
}

Agora você pode descomentar a seguinte linha no main método:

todo = readData(connection);

A execução da classe principal agora deve produzir a seguinte saída:

[INFO   ] Loading application properties
[INFO   ] Connecting to the database
[INFO   ] Database connection test: demo
[INFO   ] Create database schema
[INFO   ] Insert data
[INFO   ] Read data
[INFO   ] Data read from the database: Todo{id=1, description='configuration', details='congratulations, you have set up JDBC correctly!', done=true}
[INFO   ] Closing database connection

Atualizar os dados em Azure HorizonDB

Vamos atualizar os dados que inserimos anteriormente.

Ainda no arquivo src/main/java/DemoApplication.java , após o readData método, adicione o seguinte método para atualizar dados dentro do banco de dados:

private static void updateData(Todo todo, Connection connection) throws SQLException {
    log.info("Update data");
    PreparedStatement updateStatement = connection
            .prepareStatement("UPDATE todo SET description = ?, details = ?, done = ? WHERE id = ?;");

    updateStatement.setString(1, todo.getDescription());
    updateStatement.setString(2, todo.getDetails());
    updateStatement.setBoolean(3, todo.isDone());
    updateStatement.setLong(4, todo.getId());
    updateStatement.executeUpdate();
    readData(connection);
}

Agora pode descomentar as duas linhas seguintes no método main.

todo.setDetails("congratulations, you have updated data!");
updateData(todo, connection);

A execução da classe principal agora deve produzir a seguinte saída:

[INFO   ] Loading application properties
[INFO   ] Connecting to the database
[INFO   ] Database connection test: demo
[INFO   ] Create database schema
[INFO   ] Insert data
[INFO   ] Read data
[INFO   ] Data read from the database: Todo{id=1, description='configuration', details='congratulations, you have set up JDBC correctly!', done=true}
[INFO   ] Update data
[INFO   ] Read data
[INFO   ] Data read from the database: Todo{id=1, description='configuration', details='congratulations, you have updated data!', done=true}
[INFO   ] Closing database connection

Eliminar dados no Azure HorizonDB

Por fim, vamos apagar os dados que inserimos anteriormente.

Ainda no arquivo src/main/java/DemoApplication.java , após o updateData método, adicione o seguinte método para excluir dados dentro do banco de dados:

private static void deleteData(Todo todo, Connection connection) throws SQLException {
    log.info("Delete data");
    PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM todo WHERE id = ?;");
    deleteStatement.setLong(1, todo.getId());
    deleteStatement.executeUpdate();
    readData(connection);
}

Agora você pode descomentar a seguinte linha no main método:

deleteData(todo, connection);

A execução da classe principal agora deve produzir a seguinte saída:

[INFO   ] Loading application properties
[INFO   ] Connecting to the database
[INFO   ] Database connection test: demo
[INFO   ] Create database schema
[INFO   ] Insert data
[INFO   ] Read data
[INFO   ] Data read from the database: Todo{id=1, description='configuration', details='congratulations, you have set up JDBC correctly!', done=true}
[INFO   ] Update data
[INFO   ] Read data
[INFO   ] Data read from the database: Todo{id=1, description='configuration', details='congratulations, you have updated data!', done=true}
[INFO   ] Delete data
[INFO   ] Read data
[INFO   ] There is no data in the database!
[INFO   ] Closing database connection

Limpeza de recursos

Parabéns! Criou uma aplicação Java que usa JDBC para armazenar e recuperar dados de uma instância do Azure HorizonDB.

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