todos テーブルを作成し、Spring Data JPA の REST コントローラーで作成・読み取り・更新・削除の各リクエストを処理します。すべての接続には、証明書を完全に検証する TLS が使用されます。
前提条件
- JDK 17 以降 (Spring Boot 4 の要件) 。本ガイドは Java 25、Spring Boot 4.1.1、pgjdbc 42.7.13、Hibernate 7.4.5、HikariCP 7.0.2、Flyway 12.4.0 で動作確認しています。
- ClickHouse Cloud アカウント
- データベース作成用の
psql。CREATE DATABASEステートメントは SQL コンソール で実行することもできます。
ClickHouse Managed Postgres サービスを作成する
ClickHouse Cloud コンソールで New service をクリックし、Postgres を選択します。インスタンスは数分で利用可能になります。詳しい手順については、クイックスタートを参照してください。接続情報を取得する
サービスの左サイドバーで Connect をクリックします。表示されるモーダルには、ユーザー名、パスワード、サーバー、ポートが表示されるほか、Directly / via PgBouncer を切り替えるトグルがあります。 Directly を選択した状態で Use SSL をオンにし、jdbc タブを開きます。ここに表示される JDBC URL は、2 か所を変更すれば Spring Boot で使用できます。変更するのはデータベース名と CA 証明書ファイル名で、この作業は 接続を設定する で行います。5432 で Postgres に直接接続します。Spring Boot アプリは長時間稼働するプロセスで、独自の HikariCP pool (デフォルトで 10 接続) を保持するため、Postgres の前段に別のプーラーを置く必要はありません。アプリのインスタンス数と pool サイズを掛けた値が、サービスの max_connections を超えないようにしてください。多数のインスタンスを実行する場合は、PgBouncer を使用するを参照してください。
モーダルで Download CA certificate をクリックします。Settings → CA Certificate からもダウンロードできます。この証明書はインスタンスごとに固有のものであるため、ドライバーはこれを使って接続先が自分のサーバーであることを検証できます。
プロジェクトをセットアップする
Spring Initializr で、依存関係として Spring Web、Spring Data JPA、Flyway Migration、PostgreSQL Driver を追加した Maven プロジェクトを生成します。接続を設定する
ダウンロードした CA 証明書をプロジェクトのディレクトリに移動し、ファイル名をca-certificate.pem にリネームします。
アプリ用のデータベースを作成します。<PASSWORD> とホストは、Connect モーダルに表示される値に置き換えてください:
src/main/resources/application.properties の内容を以下に置き換えてください。既存のアプリケーションの場合は、spring.datasource と spring.jpa の行を追加し、H2 の URL など、その他の spring.datasource 設定はすべて削除してください。URL には jdbc タブに表示されているものを使用し、データベースを guide_spring に、sslrootcert をリネームしたファイルに変更しています。
src/main/resources/application.properties
sslmode=verify-full と sslrootcert を読み取ります。CA ファイル内のすべての証明書を読み込み、証明書チェーンとホスト名の両方を検証するため、TLS 用のコードを別途記述する必要はありません。Spring Boot はパスワードを DATABASE_PASSWORD 環境変数から読み取るため、パスワードをファイルに記載せずに済みます。
その他の設定は次のとおりです。
ddl-auto=validateを指定すると、Hibernate は起動時に、エンティティが Flyway によって作成されたテーブルと一致しているかを検証します。Hibernate がスキーマを変更することはありません。open-in-view=falseを指定すると、各接続は HTTP レスポンスの書き込み完了まで保持されず、トランザクションの終了時点で pool に返却されます。
sslrootcert に相対パスを指定した場合、JVM の作業ディレクトリを基準に解決されます。./mvnw spring-boot:run はプロジェクトディレクトリでアプリを起動するため、この場合は ca-certificate.pem のままで動作します。パッケージ化した JAR を別の場所で実行する場合は、絶対パスを指定してください。pgjdbc は ~ を展開しません。Use SSL をオフにした状態で URL をコピーすると sslmode が含まれないため、pgjdbc は prefer を使用します。この場合、接続は暗号化されますが、サーバーの検証は行われません。CA が正しくない場合、接続は PKIX path building failed で失敗します。ファイルが存在しない場合は Could not open SSL root certificate file で失敗します。IP アドレスで接続した場合など、ホストが証明書と一致しない場合は The hostname ... could not be verified by hostnameverifier PgjdbcHostnameVerifier で失敗します。Flyway でテーブルを作成する
Flyway は、アプリケーションの起動時に、Hibernate がスキーマを検証するよりも前にsrc/main/resources/db/migration 内の SQL ファイルをバージョン順に実行します。src/main/resources/db/migration/V1__create_todos.sql を作成します。
src/main/resources/db/migration/V1__create_todos.sql
flyway_schema_history テーブルに記録し、以降の起動時には新しいファイルのみを実行します。スキーマを変更する場合は、実行済みのファイルを編集するのではなく、V2__add_due_date.sql のような新しいファイルを追加してください。
H2 を使用していたアプリでは、通常は Hibernate にテーブルを作成させていたはずです。これは、埋め込みデータベースでは
ddl-auto のデフォルトが create-drop になるためです。Postgres ではデフォルトが none なので、既存のテーブルを作成する移行を追加してください。また、単純な @GeneratedValue を付けた @Id には、エンティティ名にちなんだシーケンス (たとえば Employee なら employee_seq) も必要です。このシーケンスは、Hibernate のデフォルトの割り当てサイズである 50 ずつ増加するように設定する必要があります。そうしないと、起動時に Schema validation: missing sequence [employee_seq] というエラーで失敗します。また、シーケンスが 1 ずつ増加する設定になっている場合は、The increment size of the [employee_seq] sequence is set to [50] in the entity mapping but the mapped database sequence increment size is [1] というエラーで失敗します。データベースにクエリを実行する
src/main/java/com/example/todos/Todo.java にエンティティを作成します。Spring Boot により、createdAt フィールドは created_at カラムにマッピングされます。
src/main/java/com/example/todos/Todo.java
src/main/java/com/example/todos/TodoRepository.java に Spring Data リポジトリを作成します。このインターフェイスの実装は、実行時に Spring Data によって自動的に生成されます。
src/main/java/com/example/todos/TodoRepository.java
src/main/java/com/example/todos/TodoController.java に REST コントローラーを作成し、ルートごとにリポジトリ呼び出しを 1 つずつ割り当てます:
src/main/java/com/example/todos/TodoController.java
@Transactional アノテーションを付けたメソッド内でそれらを呼び出します。
実行と確認
パスワードを設定して、アプリを起動します。POST リクエストは新しく作成された行を、PATCH リクエストは更新後の行を返します。-w '\n' を指定すると、各レスポンスの後に改行が出力されます。
pg_stat_ssl に対してクエリを実行します:
guide_spring、public の順に展開して、todos テーブルをクリックします。隣にある flyway_schema_history テーブルには移行履歴が記録されています。todos テーブルには、残った行が格納されています。
PgBouncer を使用する
多数のアプリインスタンスからの接続数の合計が Postgres の接続数上限に近づく場合は、アプリを同梱の PgBouncer 経由でポート6432 に接続し、Flyway は直接接続のままにしてください。application.properties で spring.datasource.url のポートを 6432 に変更し、その下に次の spring.flyway の行を追加します。
src/main/resources/application.properties
spring.flyway.url を設定すると、Flyway は独自の接続を開き、spring.datasource のユーザー名とパスワードは使用しません。そのため、spring.flyway.user と spring.flyway.password も設定してください。CA 証明書は両方のポートで同じものを使用できます。
PgBouncer はトランザクションプーリングモードで動作するため、トランザクションごとに異なる Postgres 接続上で実行される可能性があります。Spring Boot を使用する場合は、次の点に注意してください。
- プリペアドステートメントはデフォルトのドライバー設定のままで動作するため、
prepareThreshold=0は不要です。pgjdbc は、同じ接続上でステートメントが 5 回目に実行された時点 (prepareThreshold) から、名前付きのサーバー側プリペアドステートメントを使用します。ステートメントはプロトコルレベルのCloseメッセージで解放され、同梱の PgBouncer はこのメッセージに対応しています。 - pgjdbc 42.7.5 以降が必要です。それより古いバージョンは起動パラメータ
extra_float_digitsを送信するため、PgBouncer がFATAL: unsupported startup parameter: extra_float_digitsというエラーで接続を拒否します。Spring Boot 4 には新しいバージョンの pgjdbc が含まれています。それより古い Spring Boot では、pom.xmlの<properties>セクションに<postgresql.version>42.7.13</postgresql.version>を指定してください。 - セッション設定は保持されません。
SETステートメントやConnection.setSchema(HikariCP のspring.datasource.hikari.schemaプロパティが内部で使用) は、それを実行した Postgres 接続にのみ適用され、他のクライアントに影響が及ぶおそれがあります。URL パラメータのcurrentSchemaとoptionsはFATAL: unsupported startup parameterで失敗します。スキーマ修飾したテーブル名を使用するか、トランザクション内でSET LOCALを使用してください。 - Flyway はトランザクションレベルのアドバイザリロックを取得するため、デフォルトのままで PgBouncer 経由でも動作します。
CREATE INDEX CONCURRENTLYを実行する移行ではspring.flyway.postgresql.transactional-lock=falseが必要で、この設定により Flyway はセッションレベルのロックを使用するようになります。PgBouncer 経由ではこのロックがUnable to release PostgreSQL advisory lockで失敗し、pool内の接続上で保持されたままになるため、Flyway には直接接続を使用してください。 - poolサイズ: HikariCP の各接続は、PgBouncer への低コストなクライアント接続です。PgBouncer は、アプリのインスタンスが開くクライアント接続の数にかかわらず、データベースとユーザーの組み合わせごとに開く Postgres 接続の数を制限します (
default_pool_size) 。この上限を超えたトランザクションは、HikariCP で失敗するのではなく、PgBouncer 内で最大query_wait_timeout(デフォルトは 120 秒) まで待機します。HikariCP のpoolを大きくしても同時実行性が向上するのはこの上限までなので、インスタンスあたり 10 というデフォルト値を始点にするのが適切です。詳しくは PgBouncer と Postgres の接続上限を参照してください。
pg_stat_ssl が報告するのは PgBouncer と Postgres 間の接続であるため、実行と検証の TLS クエリには、PgBouncer を経由するアプリの接続は表示されません。ただし、アプリから PgBouncer への接続も検証は行われており、誤った CA 証明書を使用した場合は同じように失敗します。