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,用于创建数据库。也可以在 SQL 控制台 中执行CREATE DATABASE语句。
创建 ClickHouse Managed Postgres 服务
在 ClickHouse Cloud 控制台中,点击 New service,然后选择 Postgres。实例会在几分钟内准备就绪。完整操作步骤请参阅快速入门。获取连接信息
在服务的左侧边栏中点击 Connect。弹出的对话框会显示您的用户名、密码、服务器和端口,并提供 Directly / via PgBouncer 切换开关。 选中 Directly,开启 Use SSL,然后打开 jdbc 选项卡。此处会显示一个 JDBC URL,只需修改两处即可在 Spring Boot 中使用:数据库名称和 CA 证书文件名。具体修改步骤见配置连接。5432 上的 Postgres。Spring Boot 应用是长期运行的进程,会维护自己的 HikariCP 连接池 (默认 10 个连接) ,因此无需在 Postgres 前面再部署一个连接池器。请确保应用实例数与连接池大小的乘积低于服务的 max_connections。如果您运行的实例较多,请参阅使用 PgBouncer。
在弹出的对话框中,点击 Download CA certificate。您也可以在 Settings → CA Certificate 中下载该证书。该证书是您的实例所独有的,因此驱动程序可以借此确认其连接的确实是您的服务器。
创建项目
使用 Spring Initializr 生成一个 Maven 项目,并添加 Spring Web、Spring Data JPA、Flyway Migration 和 PostgreSQL Driver 依赖项:配置连接
将下载的 CA 证书移动到项目目录中,并重命名为ca-certificate.pem。
为应用创建一个数据库。将 <PASSWORD> 和主机地址替换为 Connect 对话框中显示的值:
src/main/resources/application.properties 的内容替换为以下内容。如果是现有应用,请添加 spring.datasource 和 spring.jpa 相关配置行,并删除其他所有 spring.datasource 设置 (例如 H2 URL) 。此处的 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 不会修改 schema。open-in-view=false会在事务结束时立即将连接归还到连接池,而不是一直占用到 HTTP 响应写入完成。
相对的
sslrootcert 路径会基于 JVM 的工作目录进行解析。./mvnw spring-boot:run 会在项目目录中启动应用,因此此时使用 ca-certificate.pem 即可正常工作。若在其他位置运行打包后的 JAR,请使用绝对路径。注意,pgjdbc 不会展开 ~。如果复制 URL 时关闭了 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 校验 schema 之前,按版本顺序执行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 表中记录每个已应用的文件,后续启动时只运行新文件。如需更改 schema,请添加一个新文件 (例如 V2__add_due_date.sql) ,而不要修改已运行过的文件。
使用 H2 的应用通常由 Hibernate 自动创建表,因为对于嵌入式数据库,
ddl-auto 的默认值为 create-drop。而对于 Postgres,其默认值为 none,因此需要添加一个迁移脚本来创建现有的表。如果 @Id 仅使用普通的 @GeneratedValue,还需要一个以实体命名的序列 (例如 Employee 对应 employee_seq) ,且其步长为 50,即 Hibernate 的默认分配大小。否则,启动时会报错 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 repository。Spring Data 会在运行时自动实现该接口:
src/main/java/com/example/todos/TodoRepository.java
src/main/java/com/example/todos/TodoController.java 中创建一个 REST 控制器,每个路由调用一次 repository:
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。同一条语句在同一连接上执行到第五次时 (由prepareThreshold决定) ,pgjdbc 会开始使用具名的服务端预处理语句。pgjdbc 通过协议层的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 连接生效,并且可能泄漏给其他客户端。使用currentSchema和optionsURL 参数会报错FATAL: unsupported startup parameter。请使用带 schema 限定的表名,或在事务内使用SET LOCAL。 - Flyway 默认可以通过 PgBouncer 正常工作,因为它获取的是事务级咨询锁。如果迁移中包含
CREATE INDEX CONCURRENTLY,则需要设置spring.flyway.postgresql.transactional-lock=false,这会让 Flyway 改用会话级锁。经由 PgBouncer 时,该锁会因Unable to release PostgreSQL advisory lock而失败,并一直被某个池化连接持有,因此请让 Flyway 使用直接连接。 - 连接池大小:每个 HikariCP 连接都只是一个到 PgBouncer 的低开销客户端连接。无论应用实例打开多少客户端连接,PgBouncer 为每个数据库和用户打开的 Postgres 连接数都受
default_pool_size限制。超出该限制的事务会在 PgBouncer 中排队等待,最长可达query_wait_timeout(默认 120 秒) ,而不会直接在 HikariCP 中失败。增大 HikariCP 连接池最多只能将并发度提升到该限制,因此每个实例默认的 10 个连接是不错的起点。请参阅 PgBouncer 与 Postgres 连接数限制。
pg_stat_ssl 反映的是 PgBouncer 与 Postgres 之间的连接,因此 运行并验证 中的 TLS 查询不会显示应用经由 PgBouncer 建立的连接。但应用到 PgBouncer 的连接仍会经过验证:如果 CA 证书有误,连接同样会失败。