Rediger

Quickstart: Use Java and JDBC in Azure Database for PostgreSQL flexible server

This article shows how to create a sample application that uses Java and JDBC to store and retrieve information in Azure Database for PostgreSQL.

JDBC is the standard Java API for connecting to traditional relational databases.

This article includes two authentication methods: Microsoft Entra authentication and PostgreSQL authentication. The Passwordless tab shows the Microsoft Entra authentication and the Password tab shows the PostgreSQL authentication.

Microsoft Entra authentication is a mechanism for connecting to Azure Database for PostgreSQL using identities defined in Microsoft Entra ID. By using Microsoft Entra authentication, you can manage database user identities and other Microsoft services in a central location, which simplifies permission management.

PostgreSQL authentication uses accounts stored in PostgreSQL. If you choose to use passwords as credentials for the accounts, store these credentials in the user table. Because these passwords are stored in PostgreSQL, you need to manage the rotation of the passwords yourself.

Prerequisites

Prepare the working environment

First, use the following command to set up some environment variables.

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_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)

Replace the placeholders with the following values, which are used throughout this article:

  • <YOUR_DATABASE_SERVER_NAME>: The name of your Azure Database for PostgreSQL flexible server, which should be unique across Azure.
  • <YOUR_DATABASE_NAME>: The database name of the Azure Database for PostgreSQL flexible server, which should be unique within Azure.
  • <YOUR_AZURE_REGION>: The Azure region to use. You can use eastus by default, but choose a region closer to where you live. To see the full list of available regions, enter az account list-locations.
  • <YOUR_POSTGRESQL_AD_NON_ADMIN_USERNAME>: The username for your Azure Database for PostgreSQL flexible server. Ensure the username is a valid user in your Microsoft Entra tenant.
  • <YOUR_LOCAL_IP_ADDRESS>: The IP address of your local computer, from which you run your Spring Boot application. One convenient way to find it is to open whatismyip.akamai.com.

Important

When setting <YOUR_POSTGRESQL_AD_NON_ADMIN_USERNAME>, the user must already exist in your Microsoft Entra tenant or you can't create a Microsoft Entra user in your database.

Next, create a resource group by using the following command:

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

Create a flexible server

The following sections describe how to create and configure your database instance.

Create a flexible server and set up admin user

Note

For more detailed information about creating Azure Database for PostgreSQL flexible servers, see Quickstart: Create an Azure Database for PostgreSQL flexible server.

If you're using Azure CLI, run the following command to make sure it has sufficient permission:

az login --scope https://graph.microsoft.com/.default

Run the following command to create the server:

az postgres flexible-server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_SERVER_NAME \
    --location $AZ_LOCATION \
    --yes \
    --output tsv

To set up a Microsoft Entra administrator after creating the server, follow the steps in Manage Microsoft Entra roles in Azure Database for PostgreSQL.

Important

When you set up an administrator, you add a new user with full administrator privileges to the Azure Database for PostgreSQL flexible server's Azure database. You can create multiple Microsoft Entra administrators per Azure Database for PostgreSQL flexible server.

Having any issues? Let us know.

Configure a firewall rule for your Azure Database for PostgreSQL instance

Azure Database for PostgreSQL flexible servers are secure by default. They have a firewall that blocks all incoming connections. To use your database, add a firewall rule that grants your local IP address access to the database server.

Because you configured your local IP address at the beginning of this article, you can open the server's firewall by running the following command:

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

If you're connecting to your Azure Database for PostgreSQL flexible server from Windows Subsystem for Linux (WSL) on a Windows computer, you need to add the WSL host ID to your firewall.

Get the IP address of your host machine by running the following command in WSL:

cat /etc/resolv.conf

Copy the IP address that follows the term nameserver, and then use the following command to set an environment variable for the WSL IP Address:

AZ_WSL_IP_ADDRESS=<the-copied-IP-address>

Then, use the following command to open the server's firewall to your WSL-based app:

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

Configure an Azure Database for PostgreSQL database

Create a new database by using the following command:

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

Create an Azure Database for PostgreSQL non-admin user and grant permission

Next, create a non-admin user and grant all permissions to the database.

Note

For more detailed information about managing Azure Database for PostgreSQL users, see Manage Microsoft Entra roles in Azure Database for PostgreSQL.

Create a SQL script named create_ad_user.sql for creating a non-admin user. Add the following contents and save it locally:

cat << EOF > create_ad_user.sql
select * from pgaadauth_create_principal('$AZ_POSTGRESQL_AD_NON_ADMIN_USERNAME', false, false);
EOF

Then, use the following command to run the SQL script and create the Microsoft Entra non-admin user:

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

Use the following command to remove the temporary SQL script file:

rm create_ad_user.sql

Create a new Java project

Using your favorite IDE, create a new Java project by using Java 8 or later. Add a pom.xml file in the root directory with the following contents:

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

This file is an Apache Maven file that configures your project to use:

  • Java 8
  • A recent PostgreSQL driver for Java

Prepare a configuration file to connect to Azure Database for PostgreSQL

Create a src/main/resources/application.properties file, and add the following contents:

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

Note

The configuration property url includes ?sslmode=require to ensure that the JDBC driver uses TLS (Transport Layer Security) when connecting to the database. Using TLS is mandatory with Azure Database for PostgreSQL flexible servers and is a recommended security practice.

Create a SQL file to generate the database schema

Use a src/main/resources/schema.sql file to create a database schema. Create that file, and add the following content:

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

Code the application

The following sections walk through writing the Java code that connects to the database, defines a data model, and performs basic CRUD operations.

Connect to the database

Next, add the Java code that uses JDBC to store and retrieve data from your Azure Database for PostgreSQL flexible server.

Create a src/main/java/DemoApplication.java file and add the following contents:

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

Having any issues? Let us know.

This Java code uses the application.properties and the schema.sql files you created earlier to connect to the Azure Database for PostgreSQL flexible server and create a schema that stores the data.

In this file, the methods to insert, read, update, and delete data are commented out. The rest of this article walks through coding those methods, and you can uncomment them one after the other.

Note

The database credentials are stored in the user and password properties of the application.properties file. The code uses those credentials when executing DriverManager.getConnection(properties.getProperty("url"), properties);, as the properties file is passed as an argument.

You can now execute this main class with your favorite tool:

  • Using your IDE, you can right-click on the DemoApplication class and execute it.
  • Using Maven, you can run the application by executing: mvn exec:java -Dexec.mainClass="com.example.demo.DemoApplication".

The application connects to the Azure Database for PostgreSQL flexible server, creates a database schema, and then closes the connection, as you see in the console logs:

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

Create a domain class

Create a new Todo Java class next to the DemoApplication class, and add the following code:

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

This class is a domain model mapped to the todo table that you created when executing the schema.sql script.

Insert data into Azure Database for PostgreSQL

In the src/main/java/DemoApplication.java file, after the main method, add the following method to insert data into the database:

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

You can now uncomment the two following lines in the main method:

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

When you execute the main class, it should produce the following output:

[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

Read data from Azure Database for PostgreSQL

To validate that your code works correctly, read the data you previously inserted.

In the src/main/java/DemoApplication.java file, after the insertData method, add the following method to read data from the database:

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

You can now uncomment the following line in the main method:

todo = readData(connection);

When you execute the main class, it should produce the following output:

[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

Update data in Azure Database for PostgreSQL

To update the data, use the following steps.

Still in the src/main/java/DemoApplication.java file, after the readData method, add the following method to update data inside the database:

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

You can now uncomment the two following lines in the main method:

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

When you execute the main class, it should produce the following output:

[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

Delete data in Azure Database for PostgreSQL

Finally, delete the data you previously inserted.

Still in the src/main/java/DemoApplication.java file, after the updateData method, add the following method to delete data inside the database:

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

You can now uncomment the following line in the main method:

deleteData(todo, connection);

When you execute the main class, it should produce the following output:

[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

Clean up resources

You created a Java application that uses JDBC to store and retrieve data from an Azure Database for PostgreSQL flexible server.

To clean up all resources used during this quickstart, delete the resource group by using the following command:

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