快速入门:将 Java和 JDBC 与 Azure Database for PostgreSQL 配合使用

适用于:Azure Database for PostgreSQL 单一服务器

重要

Azure Database for PostgreSQL - 单一服务器即将停用。 强烈建议升级到 Azure Database for PostgreSQL - 灵活服务器。 有关迁移到 Azure Database for PostgreSQL 灵活服务器的详细信息,请参阅 Azure Database for PostgreSQL 单一服务器的最新动态?

本文演示如何创建一个使用 Java 和 JDBCAzure Database for PostgreSQL 中存储和检索信息的示例应用程序。

JDBC 是标准的 Java API,用于连接到传统的关系数据库。

在本文中,我们将讨论两种身份验证方法:Microsoft Entra 身份验证和 PostgreSQL 身份验证。 “无密码”选项卡可显示 Microsoft Entra 身份验证,“密码”选项卡则显示 PostgreSQL 身份验证

Microsoft Entra 身份验证是一种使用 Microsoft Entra ID 中定义的标识连接到 Azure Database for PostgreSQL 的机制。 使用 Microsoft Entra 身份验证,可以在一个中心位置管理数据库用户标识和其他 Azure 服务,从而简化权限管理。

PostgreSQL 身份验证使用存储在 PostgreSQL 中的帐户。 如果你选择使用密码作为帐户的凭据,这些凭据将存储在 user 表中。 由于这些密码存储在 PostgreSQL 中,因此你需要自行管理密码的轮换。

先决条件

准备工作环境

首先设置一些环境变量。

export AZ_RESOURCE_GROUP=database-workshop
export AZ_DATABASE_SERVER_NAME=<YOUR_DATABASE_SERVER_NAME>
export AZ_DATABASE_NAME=demo
export AZ_LOCATION=<YOUR_AZURE_REGION>
export AZ_POSTGRESQL_AD_NON_ADMIN_USERNAME=<YOUR_POSTGRESQL_AD_NON_ADMIN_USERNAME>
export AZ_LOCAL_IP_ADDRESS=<YOUR_LOCAL_IP_ADDRESS>
export CURRENT_USERNAME=$(az ad signed-in-user show --query userPrincipalName -o tsv)
export CURRENT_USER_OBJECTID=$(az ad signed-in-user show --query id -o tsv)

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

  • <YOUR_DATABASE_SERVER_NAME>:PostgreSQL 服务器的名称,应在整个 Azure 中保持唯一。
  • <YOUR_AZURE_REGION>:将使用的 Azure 区域。 默认情况下可以使用 chinaeast2,但我们建议你配置一个离居住位置更近的区域。 可以通过输入 az account list-locations 查看可用区域的完整列表。
  • <YOUR_POSTGRESQL_AD_NON_ADMIN_USERNAME>:PostgreSQL 数据库服务器的用户名。 确保用户名是 Microsoft Entra 租户中的有效用户。
  • <YOUR_LOCAL_IP_ADDRESS>:你将在其中运行 Spring Boot 应用程序的本地计算机的 IP 地址。 一种查看 IP 地址的便捷方法是打开 whatismyip.akamai.com

重要

在设置 <YOUR_POSTGRESQL_AD_NON_ADMIN_USERNAME> 时,用户名必须已存在于 Microsoft Entra 租户中,否则将无法在数据库中创建 Microsoft Entra 用户。

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

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

创建 Azure Database for PostgreSQL 实例

以下部分介绍了如何创建和配置数据库实例。

创建 PostgreSQL 服务器并设置管理员用户

首先需要创建具有管理员用户的托管 PostgreSQL 服务器。

注意

可以在使用 Azure 门户创建 Azure Database for PostgreSQL 服务器中阅读有关创建 PostgreSQL 服务器的更多详细信息。

如果使用 Azure CLI,请运行以下命令以确保该用户拥有足够的权限:

az login --scope https://microsoftgraph.chinacloudapi.cn/.default

然后运行以下命令来创建服务器:

az postgres server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_SERVER_NAME \
    --location $AZ_LOCATION \
    --sku-name B_Gen5_1 \
    --storage-size 5120 \
    --output tsv

现在运行以下命令来设置 Microsoft Entra 管理员用户:

az postgres server ad-admin create \
    --resource-group $AZ_RESOURCE_GROUP \
    --server-name $AZ_DATABASE_SERVER_NAME \
    --display-name $CURRENT_USERNAME \
    --object-id $CURRENT_USER_OBJECTID

重要

设置管理员时,将向具有完全管理员权限的 Azure Database for PostgreSQL 服务器添加新用户。 每个 PostgreSQL 服务器只能创建一个 Microsoft Entra 管理员,选择另一个管理员后,将会覆盖为服务器配置的现有 Microsoft Entra 管理员。

此命令创建一个小型 PostgreSQL 服务器,并将 Active Directory 管理员设置为已登录的用户。

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

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

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

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

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

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

cat /etc/resolv.conf

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

AZ_WSL_IP_ADDRESS=<the-copied-IP-address>

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

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

配置 PostgreSQL 数据库

你在前面创建的 PostgreSQL 服务器为空。 使用以下命令创建新数据库。

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

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

接下来创建一个非管理员用户,并为其授予对数据库的所有权限。

注意

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

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

cat << EOF > create_ad_user.sql
SET aad_validate_oids_in_tenant = off;
CREATE ROLE "$AZ_POSTGRESQL_AD_NON_ADMIN_USERNAME" WITH LOGIN IN ROLE azure_ad_user;
GRANT ALL PRIVILEGES ON DATABASE $AZ_DATABASE_NAME TO "$AZ_POSTGRESQL_AD_NON_ADMIN_USERNAME";
EOF

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

psql "host=$AZ_DATABASE_SERVER_NAME.postgres.database.chinacloudapi.cn user=$CURRENT_USERNAME@$AZ_DATABASE_SERVER_NAME dbname=$AZ_DATABASE_NAME port=5432 password=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken) sslmode=require" < create_ad_user.sql

现在使用以下命令删除临时 SQL 脚本文件:

rm create_ad_user.sql

新建一个 Java 项目

使用你偏好的 IDE 在 Java 8 或更高版本中创建新的 Java 项目,并在其根目录中添加一个包含以下内容的 pom.xml 文件:

<?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>
      <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity-extensions</artifactId>
        <version>1.0.0</version>
      </dependency>
    </dependencies>
</project>

此文件是一个 Apache Maven 文件,用于将项目配置为使用 Java 8 以及适用于 Java 的最新 PostgreSQL 驱动程序。

准备用于连接到 Azure Database for PostgreSQL 的配置文件

创建 src/main/resources/application.properties 文件,然后添加以下内容:

cat << EOF > src/main/resources/application.properties
url=jdbc:postgresql://${AZ_DATABASE_SERVER_NAME}.postgres.database.chinacloudapi.cn:5432/${AZ_DATABASE_NAME}?sslmode=require&authenticationPluginClassName=com.azure.identity.extensions.jdbc.postgresql.AzurePostgresqlAuthenticationPlugin
user=${AZ_POSTGRESQL_AD_NON_ADMIN_USERNAME}@${AZ_DATABASE_SERVER_NAME}
EOF

注意

配置属性 url 中追加了 ?sslmode=require,以告知 JDBC 驱动程序在连接到数据库时使用 TLS(传输层安全性)。 必须对 Azure Database for PostgreSQL 使用 TLS,这也是一种良好的安全做法。

创建 SQL 文件以生成数据库架构

你将使用 src/main/resources/schema.sql 文件来创建数据库架构。 创建该文件,然后添加以下内容:

cat << EOF > src/main/resources/schema.sql
DROP TABLE IF EXISTS todo;
CREATE TABLE todo (id SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);
EOF

编写应用程序代码

连接到数据库

接下来,添加将使用 JDBC 在 PostgreSQL 服务器中存储和检索数据的 Java 代码。

创建 src/main/java/DemoApplication.java 文件,然后添加以下内容:

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

        /* Prepare for data processing in the PostgreSQL server.
        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();
    }
}

此 Java 代码将使用前面创建的 application.properties 和 schema.sql 文件连接到 PostgreSQL 服务器并创建用于存储数据的架构。

在此文件中,可以看到我们注释了用于插入、读取、更新和删除数据的方法。 在本文的余下部分,你将编写这些方法的代码,并可以逐个取消注释这些方法。

注意

数据库凭据存储在 application.properties 文件的 userpassword 属性中。 执行 DriverManager.getConnection(properties.getProperty("url"), properties); 时使用这些凭据,因为属性文件是作为参数传递的。

现在可以通过喜欢的工具执行此主类:

  • 使用你的 IDE,你应该能够右键单击 DemoApplication 类并执行它。
  • 在 Maven 中,可以使用以下命令运行应用程序:mvn exec:java -Dexec.mainClass="com.example.demo.DemoApplication"

应用程序应连接到 Azure Database for PostgreSQL,创建数据库架构,然后关闭连接,如控制台日志中所示:

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

创建域类

DemoApplication 类旁创建新的 Todo Java 类并添加以下代码:

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

此类是在执行 schema .sql 脚本时创建的 todo 表上映射的域模型。

将数据插入 Azure Database for PostgreSQL

在 src/main/java/DemoApplication.java 文件中,在 main 方法之后添加以下方法,以将数据插入数据库:

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

你现在可以对 main 方法中的以下两行执行取消注释操作:

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

现在,执行主类应会生成以下输出:

[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

从 Azure Database for PostgreSQL 读取数据

若要验证代码是否正常运行,请读取先前插入的数据。

在 src/main/java/DemoApplication.java 文件中,在 insertData 方法之后添加以下方法,以从数据库中读取数据:

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

你现在可以对 main 方法中的以下行执行取消注释操作:

todo = readData(connection);

现在,执行主类应会生成以下输出:

[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 

更新 Azure Database for PostgreSQL 中的数据

接下来,更新先前插入的数据。

在 src/main/java/DemoApplication.java 文件中,在 readData 方法之后添加以下方法,以更新数据库中的数据:

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

你现在可以对 main 方法中的以下两行执行取消注释操作:

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

现在,执行主类应会生成以下输出:

[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 

删除 Azure Database for PostgreSQL 中的数据

最后,删除之前插入的数据。

在 src/main/java/DemoApplication.java 文件中,在 updateData 方法之后添加以下方法,以删除数据库中的数据:

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

你现在可以对 main 方法中的以下行执行取消注释操作:

deleteData(todo, connection);

现在,执行主类应会生成以下输出:

[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 

清理资源

祝贺你! 你已创建了一个 Java 应用程序,该应用程序使用 JDBC 在 Azure Database for PostgreSQL 中存储和检索数据。

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

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

后续步骤