クイックスタート: Azure Database for MySQL で Java と JDBC を使用する

適用対象: Azure Database for MySQL - シングル サーバー

重要

Azure Database for MySQL の単一サーバーは提供終了パスにあります。 Azure Database for MySQL フレキシブル サーバーにアップグレードすることを強くお勧めします。 Azure Database for MySQL フレキシブル サーバーへの移行の詳細については、「Azure Database for MySQL 単一サーバーの動作」を参照してください

この記事では、Java と JDBC を使って Azure Database for MySQL に情報を格納および取得するサンプル アプリケーションを作成する方法を説明します。

JDBC は、従来のリレーショナル データベースに接続するための標準の Java API です。

この記事には、Microsoft Entra 認証と MySQL 認証の 2 つの認証方法が含まれています。 [パスワードレス] タブには Microsoft Entra 認証が表示され、[パスワード] タブには MySQL 認証が表示されます。

Microsoft Entra 認証は、Microsoft Entra ID で定義された ID を使用して Azure Database for MySQL に接続するための仕組みです。 Microsoft Entra 認証を使用すると、データベース ユーザーの ID や他の Microsoft サービスを一元管理でき、アクセス許可の管理が容易になります。

MySQL 認証では、MySQL に格納されているアカウントが使用されます。 アカウントの資格情報としてパスワードを使用することを選択した場合、これらの資格情報は user テーブルに格納されます。 これらのパスワードは MySQL に格納されるため、パスワードのローテーションを自分で管理する必要があります。

前提条件

  • Azure アカウント。 所有していない場合は、無料試用版を入手してください。
  • Azure Cloud Shell または Azure CLI。 Azure Cloud Shell をお勧めします。これにより、自動的にログインし、必要なすべてのツールにアクセスできるようになります。
  • サポートされている Java 開発キット、バージョン 8 (Azure Cloud Shell に含まれます)。
  • Apache Maven ビルド ツール。
  • MySQL コマンド ライン クライアント。 Azure Cloud Shell で、mysql.exe コマンドライン ツールを使用して、サーバーに接続できます。 あるいは、ローカル環境で mysql コマンド ラインを使用することもできます。

作業環境を準備する

まず、環境変数をいくつか設定します。 Azure Cloud Shell で次のコマンドを実行します。

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_MYSQL_AD_NON_ADMIN_USERNAME=demo-non-admin
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>: MySQL サーバーの名前。Azure 全体で一意である必要があります。
  • <YOUR_AZURE_REGION>:使用する Azure リージョン。 既定で eastus を使用できますが、居住地に近いリージョンを構成することをお勧めします。 az account list-locations と入力すると、使用可能なリージョンの一覧全体を表示できます。
  • <YOUR_LOCAL_IP_ADDRESS>: ローカル コンピューターの IP アドレス。そこから、アプリケーションを実行します。 これを見つける簡単な方法は、ブラウザーで whatismyip.akamai.com にアクセスすることです。

次に、次のコマンドを使用し、リソース グループを作成します。

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

Azure Database for MySQL インスタンスを作成する

MySQL サーバーを作成し、管理者ユーザーを設定する

最初に作成するのは、マネージド MySQL サーバーです。

注意

MySQL サーバーの作成に関する詳細については、「クイックスタート: Azure portal を使用した Azure Database for MySQL サーバーの作成」を参照してください。

Azure CLI を使用する場合は、次のコマンドを実行して、十分なアクセス許可があることを確認します。

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

次に、次のコマンドを実行してサーバーを作成します。

az mysql 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 mysql 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 MySQL サーバーに追加されます。 作成できる Microsoft Entra 管理者は、MySQL サーバーあたり 1 人だけです。 別のユーザーを選択すると、そのサーバーに構成されている既存の Microsoft Entra 管理者が上書きされます。

このコマンドにより、小さな MySQL サーバーが作成され、Active Directory 管理者がサインインしているユーザーに設定されます。

MySQL サーバーのファイアウォール規則を構成する

Azure Database for MySQL インスタンスは、既定でセキュリティ保護されています。 これらのインスタンスには、受信接続を一切許可しないファイアウォールがあります。 データベースを使用できるようにするには、データベース サーバーにアクセスするためのローカル IP アドレスを許可するファイアウォール規則を追加する必要があります。

この記事の冒頭でローカル IP アドレスを構成したので、次のコマンドを実行してサーバーのファイアウォールを開くことができます。

az mysql 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) から MySQL サーバーに接続する場合、WSL のホスト ID をファイアウォールに追加する必要があります。

WSL で以下のコマンドを実行して、ホスト マシンの IP アドレスを取得します。

cat /etc/resolv.conf

nameserver の後に続く IP アドレスをコピーし、次のコマンドで WSL の IP アドレスを環境変数に設定します。

AZ_WSL_IP_ADDRESS=<the-copied-IP-address>

次に、以下のコマンドを使って、サーバーのファイアウォールを WSL ベースのアプリに開放します。

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

MySQL データベースを構成する

先ほど作成した MySQL サーバーは空です。 次のコマンドを使用して、新しいデータベースを作成します。

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

MySQL の非管理者ユーザーを作成し、アクセス許可を付与する

次に、非管理者ユーザーを作成し、データベースに対するすべてのアクセス許可を付与します。

Note

MySQL ユーザーの作成の詳細については、Azure Database for MySQL でのユーザーの作成に関するページを参照してください。

非管理者ユーザーを作成するための create_ad_user.sql という名前の SQL スクリプトを作成します。 次の内容を追加し、ローカルに保存します。

export AZ_MYSQL_AD_NON_ADMIN_USERID=$CURRENT_USER_OBJECTID

cat << EOF > create_ad_user.sql
SET aad_auth_validate_oids_in_tenant = OFF;

CREATE AADUSER '$AZ_MYSQL_AD_NON_ADMIN_USERNAME' IDENTIFIED BY '$AZ_MYSQL_AD_NON_ADMIN_USERID';

GRANT ALL PRIVILEGES ON $AZ_DATABASE_NAME.* TO '$AZ_MYSQL_AD_NON_ADMIN_USERNAME'@'%';

FLUSH privileges;

EOF

次に、次のコマンドを使用して SQL スクリプトを実行し、Microsoft Entra の非管理者ユーザーを作成します。

mysql -h $AZ_DATABASE_SERVER_NAME.mysql.database.azure.com --user $CURRENT_USERNAME@$AZ_DATABASE_SERVER_NAME --enable-cleartext-plugin --password=$(az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken) < 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>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-identity-extensions</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</project>

このファイルは、Java 8 と Java 用の最近の MySQL ドライバーを使用するようにプロジェクトを構成する Apache Maven ファイルです。

Azure Database for MySQL に接続するための構成ファイルを準備する

プロジェクトのルート ディレクトリで次のスクリプトを実行して、src/main/resources/database.properties ファイルを作成し、構成の詳細を追加します。

mkdir -p src/main/resources && touch src/main/resources/database.properties

cat << EOF > src/main/resources/database.properties
url=jdbc:mysql://${AZ_DATABASE_SERVER_NAME}.mysql.database.azure.com:3306/${AZ_DATABASE_NAME}?sslMode=REQUIRED&serverTimezone=UTC&defaultAuthenticationPlugin=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin&authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin
user=${AZ_MYSQL_AD_NON_ADMIN_USERNAME}@${AZ_DATABASE_SERVER_NAME}
EOF

Note

アプリケーションでデータソースとして MysqlConnectionPoolDataSource クラスを使っている場合は、URL の "defaultAuthenticationPlugin=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin" を削除してください。

mkdir -p src/main/resources && touch src/main/resources/database.properties

cat << EOF > src/main/resources/database.properties
url=jdbc:mysql://${AZ_DATABASE_SERVER_NAME}.mysql.database.azure.com:3306/${AZ_DATABASE_NAME}?sslMode=REQUIRED&serverTimezone=UTC&authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin
user=${AZ_MYSQL_AD_NON_ADMIN_USERNAME}@${AZ_DATABASE_SERVER_NAME}
EOF

Note

構成プロパティ url?serverTimezone=UTC が追加されているのは、データベースへの接続時に UTC 日付形式 (協定世界時) を使用するように JDBC ドライバーに指示するためです。 そうしないと、Java サーバーではデータベースと同じ日付形式が使用されず、エラーが発生します。

データベース スキーマを生成するための SQL ファイルを作成する

次に、src/main/resources/schema.sql ファイルを使用してデータベース スキーマを作成します。 このファイルを作成し、次の内容を追加します。

touch 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 を使用して MySQL サーバーにデータを格納および取得する Java コードを追加します。

src/main/java/DemoApplication.java ファイルを作成し、次の内容を追加します。

package com.example.demo;

import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;

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("database.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 to store and retrieve data from the MySQL 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();
        AbandonedConnectionCleanupThread.uncheckedShutdown();
    }
}

この Java コードでは、前に作成した database.propertiesschema.sql ファイルを使います。 MySQL サーバーに接続した後、データを格納するためのスキーマを作成できます。

このファイルでは、データの挿入、読み取り、更新、削除を行うメソッドがコメント化されていることがわかります。 この記事の残りの部分では、これらのメソッドを実装し、次々にコメントを解除することができます。

Note

データベースの資格情報は、database.properties ファイルの userpassword プロパティに格納されます。 プロパティ ファイルは引数として渡されるため、これらの資格情報は DriverManager.getConnection(properties.getProperty("url"), properties); を実行するときに使用されます。

注意

末尾の AbandonedConnectionCleanupThread.uncheckedShutdown(); 行は、アプリケーションのシャットダウン時に内部スレッドを破棄するための、MySQL ドライバー コマンドです。 この行は無視してかまいません。

以後、このメイン クラスは、次の任意のツールを使用して実行することができます。

  • IDE を使用する場合: DemoApplication クラスを右クリックして実行します。
  • Maven を使用する場合、コマンド mvn exec:java -Dexec.mainClass="com.example.demo.DemoApplication" を使用してアプリケーションを実行できます。

このアプリケーションは、Azure Database for MySQL に接続してデータベース スキーマを作成した後、接続を閉じます。 コンソール ログには、次の例のような出力が表示されます。

[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 MySQL に挿入する

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 メソッドの次の 2 つの行のコメントを解除できます。

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 MySQL からのデータの読み取り

次に、先ほど挿入したデータを読み取って、コードが正しく動作することを確認します。

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 MySQL のデータの更新

次に、前に挿入したデータを更新します。

引き続き 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 メソッドの次の 2 つの行のコメントを解除できます。

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 MySQL のデータの削除

最後に、前に挿入したデータを削除します。

引き続き 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

リソースをクリーンアップする

お疲れさまでした。 JDBC を使用して Azure Database for MySQL にデータを格納および取得する Java アプリケーションを作成しました。

このクイックスタートで使用したすべてのリソースをクリーンアップするには、次のコマンドを使用してリソース グループを削除します。

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

次のステップ