编辑此页面

使用 Hibernate ORM 和 Jakarta Persistence

Hibernate ORM 是事实上的 Jakarta Persistence(以前称为 JPA)标准实现,它为您提供了对象关系映射器的全部功能。它在 Quarkus 中运行得非常好。

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 存储库: git clone https://github.com/quarkusio/quarkus-quickstarts.git,或者下载一个存档

解决方案位于 hibernate-orm-quickstart 目录中。

设置和配置 Hibernate ORM

在 Quarkus 中使用 Hibernate ORM 时,您无需 persistence.xml 资源来配置它。

使用这种经典的配置文件也是一种选择,但除非您有特殊的进阶需求,否则是不必要的;因此,我们将首先介绍如何在没有 persistence.xml 资源的情况下配置 Hibernate ORM。

在 Quarkus 中,您只需要

  • 将您的配置设置添加到 application.properties

  • 像往常一样用 @Entity 和其他任何映射注解来注解您的实体

其他配置需求已实现自动化:Quarkus 将会做出一些有倾向性的选择和合理的猜测。

将以下依赖项添加到您的项目

  • Hibernate ORM 扩展:io.quarkus:quarkus-hibernate-orm

  • 您的 JDBC 驱动程序扩展;以下选项可用

例如

pom.xml
<!-- Hibernate ORM specific dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm</artifactId>
</dependency>

<!-- JDBC driver dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
build.gradle
// Hibernate ORM specific dependencies
implementation("io.quarkus:quarkus-hibernate-orm")

// JDBC driver dependencies
implementation("io.quarkus:quarkus-jdbc-postgresql")

@Entity 注解您的持久化对象,然后将相关的配置属性添加到 application.properties

示例 application.properties
quarkus.datasource.db-kind = postgresql (1)

quarkus.hibernate-orm.schema-management.strategy=drop-and-create (2)

%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://:5432/hibernate_db
1 配置数据源.
2 在启动时删除并创建数据库(使用 update 仅更新模式)。

请注意,这些配置属性与您典型的 Hibernate ORM 配置文件中的属性不同。它们通常会映射到 Hibernate ORM 的配置属性,但可能具有不同的名称,并且不一定是一对一映射的。

此外,Quarkus 会自动设置许多 Hibernate ORM 配置设置,并且通常会使用更现代的默认值。

有关可以在 application.properties 中设置的项列表,请参阅Hibernate ORM 配置属性

只要 Hibernate ORM 扩展列在您的项目依赖项中,就会根据 Quarkus datasource 配置创建一个 EntityManagerFactory

方言将根据您的数据源自动选择和配置;您可能需要将其配置为更精确地匹配您的数据库

然后,您可以愉快地注入您的 EntityManager

使用 Hibernate 的示例应用程序 bean
@ApplicationScoped
public class SantaClausService {
    @Inject
    EntityManager em; (1)

    @Transactional (2)
    public void createGift(String giftDescription) {
        Gift gift = new Gift();
        gift.setName(giftDescription);
        em.persist(gift);
    }
}
1 注入您的实体管理器,然后尽情享受吧
2 将您的 CDI bean 方法标记为 @TransactionalEntityManager 将在提交时参与并刷新。
示例实体
@Entity
public class Gift {
    private Long id;
    private String name;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

要加载 SQL 语句并在 Hibernate ORM 启动时执行,请在 resources 目录的根目录下添加一个 import.sql 文件。此脚本可以包含任何 SQL DML 语句。确保每个语句都以分号结尾。

这对于拥有现成的数据集用于您的测试或演示非常有用。

请确保将修改数据库的方法(例如 entity.persist())包装在事务中。将 CDI bean 方法标记为 @Transactional 将为您完成此操作,并使该方法成为事务边界。我们建议在您的应用程序入口点边界(例如 REST 端点控制器)这样做。

方言

支持的数据库

对于支持的数据库,不需要显式设置Hibernate ORM 方言:它将根据数据源自动选择。

默认情况下,方言配置为针对数据库的最低支持版本。

为了让 Hibernate ORM 生成更有效的 SQL,避免变通方法并利用更多数据库功能,您可以显式设置数据库版本

使用显式 db-versionapplication.properties
quarkus.datasource.db-kind = postgresql
quarkus.datasource.db-version = 14.0 (1)

%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://:5432/hibernate_db
1 设置数据库版本。Hibernate ORM 方言将针对该版本。

通常,此处设置的版本应尽可能高,但必须低于或等于应用程序将连接到的任何数据库的版本。

如上所述,版本可以通过 quarkus.datasource.db-version 配置属性显式预配置,或者由 Quarkus 构建过程隐式设置为数据库的最低支持版本。Quarkus 将尝试在启动时将此预配置版本与实际数据库版本进行检查,当实际版本较低时会导致启动失败。

这是一个安全措施:对于比配置版本旧的数据库版本,Hibernate ORM 可能会生成无效的 SQL,这会导致运行时异常。

如果无法访问数据库,将记录一条警告消息,但启动会继续。如果知道数据库在启动时无法访问,您可以使用quarkus.hibernate-orm.database.version-check.enabled=false 来选择性地禁用版本检查。

当显式设置方言时,版本检查默认是禁用的,作为对#42255/#43703 的变通方法。

其他数据库

如果您的数据库没有相应的 Quarkus 扩展,或者默认设置由于某种原因不满足您的需求,您需要显式设置Hibernate ORM 方言

使用显式 dialectapplication.properties
quarkus.datasource.db-kind = postgresql

quarkus.hibernate-orm.dialect=Cockroach (1)

%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://:26257/hibernate_db
1 设置 Hibernate ORM 方言。

对于内置方言,期望值是官方方言列表中的名称之一,不包括 Dialect 后缀,例如 Cockroach 用于 CockroachDialect

对于第三方方言,预期值是完全限定的类名,例如 com.acme.hibernate.AcmeDbDialect

在这种情况下,请记住 JDBC 驱动程序或 Hibernate ORM 方言在 GraalVM 原生可执行文件中可能无法正常工作。

支持的数据库一样,您可以显式配置 DB 版本以充分利用 Hibernate ORM

使用显式 dialectdb-versionapplication.properties
quarkus.datasource.db-kind = postgresql
quarkus.datasource.db-version = 22.2 (1)

quarkus.hibernate-orm.dialect=Cockroach (2)

%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://:26257/hibernate_db
1 设置数据库版本。Hibernate ORM 方言将针对该版本。由于我们在这里针对 CockroachDB,因此我们传递的是 CockroachDB 版本,而不是 PostgreSQL 版本。
2 设置 Hibernate ORM 方言。

可变数据库

启用数据库多租户时,Hibernate ORM 将在运行时为同一持久化单元使用多个数据源,并且默认情况下 Quarkus 无法确定将使用哪个数据源,因此它将无法为 Hibernate ORM 检测到要使用的方言。

因此,在启用数据库多租户时,建议显式指向运行时将使用的其中一个数据源的 Hibernate ORM 配置,例如使用 quarkus.hibernate-orm.datasource=basebase 是一个数据源的名称)。

这样做时,Quarkus 将从该数据源推断数据库版本(如果可能)和方言。对于不受支持的数据库,您可能仍需要显式设置 Hibernate ORM 方言,如本节所述。

Hibernate ORM 配置属性

有各种可选属性可用于细化您的 EntityManagerFactory 或指导 Quarkus 的猜测。

没有必需的属性,只要配置了默认数据源即可。

当未设置任何属性时,Quarkus 通常可以推断出设置 Hibernate ORM 所需的所有内容,并使其使用默认数据源。

此处列出的配置属性允许您覆盖此类默认值,并自定义和调整各个方面。

构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖

配置属性

类型

默认

构建期间是否启用 Hibernate ORM。

如果在构建期间禁用了 Hibernate ORM,则所有与 Hibernate ORM 相关的处理都将被跳过,但在运行时无法激活 Hibernate ORM:quarkus.hibernate-orm.active 将默认为 false,将其设置为 true 将导致错误。

环境变量:QUARKUS_HIBERNATE_ORM_ENABLED

显示更多

布尔值

true

Hibernate ORM 是否以阻塞模式工作。

Hibernate ORM 的阻塞 EntityManager/Session/SessionFactory 通常在未找到 JDBC 数据源时默认禁用。如果您希望在拥有 JDBC 数据源的情况下禁用它们,可以将此属性设置为 false

环境变量:QUARKUS_HIBERNATE_ORM_BLOCKING

显示更多

布尔值

true

如果为 true,Quarkus 将忽略类路径中的任何 persistence.xml 文件,并完全依赖于 Quarkus 配置。

环境变量:QUARKUS_HIBERNATE_ORM_PERSISTENCE_XML_IGNORE

显示更多

布尔值

false

是否启用统计信息收集。如果“metrics.enabled”为 true,则此处默认值被认为是 true,否则默认值为 false。

环境变量:QUARKUS_HIBERNATE_ORM_STATISTICS

显示更多

布尔值

是否应为每个 Hibernate 会话将会话指标追加到服务器日志中。仅当启用了统计信息(quarkus.hibernate-orm.statistics)时才有效。默认值为 false(这意味着要使会话指标出现在日志中,statisticslog-session-metrics 都需要启用)。

环境变量:QUARKUS_HIBERNATE_ORM_LOG_SESSION_METRICS

显示更多

布尔值

如果启用了指标扩展,是否发布指标。

环境变量:QUARKUS_HIBERNATE_ORM_METRICS_ENABLED

显示更多

布尔值

false

允许在 Dev UI 页面中使用 hql 查询

环境变量:QUARKUS_HIBERNATE_ORM_DEV_UI_ALLOW_HQL

显示更多

布尔值

false

启用或禁用对 Hibernate ORM EntityManager/Session/StatelessSession 的访问*当没有事务处于活动状态*但有请求作用域时。启用后,相应的会话将是只读的。默认启用以实现向后兼容,但建议禁用此选项,以避免因在事务外运行的查询而导致的结果不一致。

环境变量:QUARKUS_HIBERNATE_ORM_REQUEST_SCOPED_ENABLED

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".datasource

此持久化单元使用的数据源的名称。

如果未定义,它将使用默认数据源。

环境变量:QUARKUS_HIBERNATE_ORM_DATASOURCE

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".packages

受此持久化单元影响的实体所在的包。

环境变量:QUARKUS_HIBERNATE_ORM_PACKAGES

显示更多

字符串列表

quarkus.hibernate-orm."持久化单元名称".sql-load-script

包含在 Hibernate ORM 启动时执行的 SQL 语句的文件的路径。

这些文件是从类路径资源中检索的,因此它们必须位于 resources 目录中(例如 src/main/resources)。

此设置的默认值取决于 Quarkus 启动模式

  • 在 dev 和 test 模式下,它默认为 import.sql。只需在您的资源目录的根目录中添加一个 import.sql 文件,它就会被拾取,无需设置此属性。传递 no-file 以强制 Hibernate ORM 忽略 SQL 导入文件。

  • 在生产模式下,它默认为 no-file。这意味着 Hibernate ORM 默认情况下不会尝试执行任何 SQL 导入文件。传递一个显式值以强制 Hibernate ORM 执行 SQL 导入文件。

如果您需要在开发模式、测试 (@QuarkusTest) 和生产之间使用不同的 SQL 语句,请使用 Quarkus 配置配置文件工具

application.properties
%dev.quarkus.hibernate-orm.sql-load-script = import-dev.sql
%test.quarkus.hibernate-orm.sql-load-script = import-test.sql
%prod.quarkus.hibernate-orm.sql-load-script = no-file

Quarkus 支持 .sql 文件,其中 SQL 语句或注释分布在多行中。每个 SQL 语句必须以分号结尾。

环境变量:QUARKUS_HIBERNATE_ORM_SQL_LOAD_SCRIPT

显示更多

字符串列表

开发和测试模式下为 import.sql;否则为 no-file

quarkus.hibernate-orm."持久化单元名称".physical-naming-strategy

用于应用数据库对象名称的物理命名规则的可插拔策略合同。 Hibernate PhysicalNamingStrategy 实现的类名

环境变量:QUARKUS_HIBERNATE_ORM_PHYSICAL_NAMING_STRATEGY

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".implicit-naming-strategy

在未给出显式名称时,用于应用隐式命名规则的可插拔策略。 Hibernate ImplicitNamingStrategy 实现的类名

环境变量:QUARKUS_HIBERNATE_ORM_IMPLICIT_NAMING_STRATEGY

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".metadata-builder-contributor

org.hibernate.boot.MetadataBuilder 公开的所有自定义选项并非都能正常工作。特别是要避免与类路径扫描相关的选项。

公开此设置主要是为了允许注册类型、转换器和 SQL 函数。

环境变量:QUARKUS_HIBERNATE_ORM_METADATA_BUILDER_CONTRIBUTOR

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".mapping-files

用于配置实体映射的 XML 文件,例如 META-INF/my-orm.xml

如果存在,则默认为 META-INF/orm.xml。 传递 no-file 以强制 Hibernate ORM 忽略 META-INF/orm.xml

环境变量:QUARKUS_HIBERNATE_ORM_MAPPING_FILES

显示更多

字符串列表

如果存在,则为 META-INF/orm.xml; 否则为 no-file

quarkus.hibernate-orm."持久化单元名称".quote-identifiers.strategy

可以使用可用的策略之一来引用标识符。

默认设置为 none,表示不引用任何标识符。如果设置为 all,所有标识符和列定义都将被引用。此外,将其设置为 all-except-column-definitions 将跳过列定义,这些列定义通常在存在时是必需的,否则使用选项 only-keywords 来仅引用被 Hibernate ORM 方言视为 SQL 关键字的标识符。

环境变量:QUARKUS_HIBERNATE_ORM_QUOTE_IDENTIFIERS_STRATEGY

显示更多

noneallall-except-column-definitionsonly-keywords

none

quarkus.hibernate-orm."持久化单元名称".second-level-caching-enabled

Quarkus 中的默认设置为启用 2 级缓存,并且已经为您集成了一个良好的实现。

只需挑出哪些实体应使用缓存。

将此设置为 false 以禁用所有 2 级缓存。

环境变量:QUARKUS_HIBERNATE_ORM_SECOND_LEVEL_CACHING_ENABLED

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".validation.mode

定义 Bean Validation 集成的行为方式。

环境变量:QUARKUS_HIBERNATE_ORM_VALIDATION_MODE

显示更多

autocallbackddlnone 列表

auto

quarkus.hibernate-orm."持久化单元名称".multitenant

定义多租户的方法(DATABASE、NONE、SCHEMA)。允许值的完整列表可在Hibernate ORM JavaDoc 中找到。DISCRIMINATOR 类型目前不支持。默认值为 NONE(无多租户)。

环境变量:QUARKUS_HIBERNATE_ORM_MULTITENANT

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".validate-in-dev-mode

如果 Hibernate 没有自动生成架构,并且 Quarkus 在开发模式下运行,则 Quarkus 将尝试在启动后验证数据库,并在出现任何问题时打印日志消息。

环境变量:QUARKUS_HIBERNATE_ORM_VALIDATE_IN_DEV_MODE

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".active

此持久化单元是否应在运行时处于活动状态。

请参阅本文档的此部分

请注意,如果禁用了 Hibernate ORM(即 quarkus.hibernate-orm.enabled 设置为 false),则所有持久化单元都将被停用,并且将此属性设置为 true 将失败。

环境变量:QUARKUS_HIBERNATE_ORM_ACTIVE

显示更多

布尔值

如果启用了 Hibernate ORM,则为“true”; 否则为“false”

quarkus.hibernate-orm."持久化单元名称".unsupported-properties."完整属性键"

应直接传递给 Hibernate ORM 的属性。 在此处使用完整的配置属性键,例如 quarkus.hibernate-orm.unsupported-properties."hibernate.order_inserts" = true

此处设置的属性完全不受支持:由于 Quarkus 通常不知道这些属性及其目的,因此不能保证它们能够正常工作,即使它们能正常工作,在升级到更新版本的 Quarkus(即使只是微补丁版本)时也可能会发生变化。

请考虑使用受支持的配置属性,然后再回退到不受支持的属性。如果没有,请务必提交功能请求,以便为 Quarkus 添加受支持的配置属性,更重要的是,以便定期测试该配置属性。

环境变量:QUARKUS_HIBERNATE_ORM_UNSUPPORTED_PROPERTIES__FULL_PROPERTY_KEY_

显示更多

Map<String,String>

数据库相关配置

类型

默认

设置后,尽最大努力尝试与给定版本的 Hibernate ORM 进行数据交换。

请注意

  • 架构验证在某些情况下可能仍然失败:这尝试使 Hibernate ORM 6+ 在运行时正确运行,但它可能仍然需要不同的(但运行时兼容的)架构。

  • 强大的测试套件仍然有用且建议:您仍然应该检查您的应用程序在旧版架构中是否按预期运行。

  • 此功能本质上是不稳定的:它的某些方面可能会在 Quarkus 的未来版本中停止工作,并且随着 Hibernate ORM 更改的堆积以及对这些旧版本的支持变得太不可靠,旧版本将被删除。

  • 您仍应计划将模式迁移到更新版本的 Hibernate ORM。有关迁移的帮助,请参阅Quarkus 3 从 Hibernate ORM 5 到 6 的迁移指南

环境变量:QUARKUS_HIBERNATE_ORM_DATABASE_ORM_COMPATIBILITY_VERSION

显示更多

5.6latest

latest

quarkus.hibernate-orm."持久化单元名称".database.charset

数据库的字符集。

用于 DDL 生成和 SQL 导入脚本。

环境变量:QUARKUS_HIBERNATE_ORM_DATABASE_CHARSET

显示更多

Charset

UTF-8

quarkus.hibernate-orm."持久化单元名称".database.default-catalog

用于数据库对象的默认目录。

环境变量:QUARKUS_HIBERNATE_ORM_DATABASE_DEFAULT_CATALOG

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".database.default-schema

用于数据库对象的默认架构。

环境变量:QUARKUS_HIBERNATE_ORM_DATABASE_DEFAULT_SCHEMA

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".database.version-check.enabled

Hibernate ORM 是否应在启动时检查数据库的版本是否与方言上配置的版本匹配(默认版本或通过 quarkus.datasource.db-version 设置的版本)。

如果数据库在启动时不可用,则应将此设置为 false

环境变量:QUARKUS_HIBERNATE_ORM_DATABASE_VERSION_CHECK_ENABLED

显示更多

布尔值

如果方言是由 Quarkus 自动设置的,则为“true,如果它是显式设置的,则为“false

方言相关配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".dialect

Hibernate ORM 方言的名称。

对于支持的数据库,此属性无需显式设置:它将根据数据源自动选择,并使用数据源上设置的 DB 版本进行配置,以获得最佳性能和最新功能。

如果您的数据库没有相应的 Quarkus 扩展,则需要显式设置此属性。 在这种情况下,请记住 JDBC 驱动程序和 Hibernate ORM 方言可能无法在 GraalVM 本机可执行文件中正常工作。

对于内置方言,期望值是官方方言列表中的名称之一,不包括 Dialect 后缀,例如 Cockroach 用于 CockroachDialect

对于第三方方言,预期值是完全限定的类名,例如 com.acme.hibernate.AcmeDbDialect

环境变量:QUARKUS_HIBERNATE_ORM_DIALECT

显示更多

字符串

自动为大多数流行的数据库选择

quarkus.hibernate-orm."持久化单元名称".dialect.storage-engine

当方言支持多个存储引擎时,要使用的存储引擎。

例如 MySQL 的 MyISAMInnoDB

环境变量:QUARKUS_HIBERNATE_ORM_DIALECT_STORAGE_ENGINE

显示更多

字符串

映射配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".mapping.timezone.default-storage

默认情况下如何为 OffsetDateTimeZonedDateTime 类型的属性在数据库中存储时区。

可以使用 @TimeZoneStorage 在每个属性的基础上覆盖此默认值。

OffsetTime 类型的属性 不受此设置的影响
default

如果支持,则等效于 native,否则等效于 normalize-utc

auto

如果支持,则等效于 native,否则等效于 column

native

将时间戳和时区存储在 timestamp with time zone 类型的列中。

仅在某些数据库/方言上可用; 如果不支持,则在静态初始化期间将引发异常。

column

将时区存储在时间戳列旁边的单独列中。

在相关实体属性上使用 @TimeZoneColumn 自定义时区列。

normalize-utc

不存储时区,并且会在持久化时丢失时区信息。

而是将该值标准化为 UTC 时区中的时间戳。

normalize

不存储时区,并且会在持久化时丢失时区信息。

相反,它将值规范化:* 持久化到数据库时,会转换为 JDBC 时区(通过 quarkus.hibernate-orm.jdbc.timezone 设置)中的时间戳,如果未设置,则为 JVM 默认时区。 * 从数据库读取时,会转换为 JVM 默认时区。

+ 使用此选项可以获取 Quarkus 2 / Hibernate ORM 5 或更旧版本的旧版行为。

环境变量:QUARKUS_HIBERNATE_ORM_MAPPING_TIMEZONE_DEFAULT_STORAGE

显示更多

nativenormalizenormalize-utccolumnautodefault

default

quarkus.hibernate-orm."持久化单元名称".mapping.id.optimizer.default

要应用于其优化器未显式配置的标识符生成器的优化器。

仅与基于表和序列的标识符生成器相关。 其他生成器(例如基于 UUID 的生成器)将忽略此设置。

优化器负责汇集新的标识符值,以减少数据库调用的频率来检索这些值,从而提高性能。

环境变量:QUARKUS_HIBERNATE_ORM_MAPPING_ID_OPTIMIZER_DEFAULT

显示更多

pooled-lo假定从表/序列检索到的值是池的下限。检索到值 N 后,新标识符池将从 NN + <allocation size> - 1(含)。, pooled假定从表/序列检索到的值是池的上限。检索到值 N 后,新标识符池将从 N - <allocation size>N + <allocation size> - 1(含)。第一个值 1 的处理方式不同,以避免负标识符。使用此项以获得 Quarkus 2 / Hibernate ORM 5 或更早版本的传统行为。, none无优化器,导致每次需要从生成器获取标识符值时都会调用数据库。不建议在生产环境中使用:可能导致性能下降和/或标识符值频繁出现间隙。

pooled-lo假定从表/序列检索到的值是池的下限。检索到值 N 后,新标识符池将从 NN + <allocation size> - 1(含)。

查询相关配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".query.query-plan-cache-max-size

查询计划缓存的最大大小。 请参阅 #org.hibernate.cfg.AvailableSettings#QUERY_PLAN_CACHE_MAX_SIZE

环境变量:QUARKUS_HIBERNATE_ORM_QUERY_QUERY_PLAN_CACHE_MAX_SIZE

显示更多

整数

2048

quarkus.hibernate-orm."持久化单元名称".query.default-null-ordering

ORDER BY 子句中 null 值的默认优先级。

有效值为:nonefirstlast

环境变量:QUARKUS_HIBERNATE_ORM_QUERY_DEFAULT_NULL_ORDERING

显示更多

nonefirstlast

none

quarkus.hibernate-orm."持久化单元名称".query.in-clause-parameter-padding

启用 IN 子句参数填充,这可以提高语句缓存。

环境变量:QUARKUS_HIBERNATE_ORM_QUERY_IN_CLAUSE_PARAMETER_PADDING

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".query.fail-on-pagination-over-collection-fetch

当无法在数据库端应用限制时,触发异常,而不是尝试性能不佳的内存结果集限制。

当分页与应用于集合或多值关联的提取连接结合使用时,限制必须在内存中应用,而不是在数据库中应用。 应避免这种情况,因为它通常具有很差的性能特征。

环境变量:QUARKUS_HIBERNATE_ORM_QUERY_FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH

显示更多

布尔值

false

JDBC 相关配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".jdbc.timezone

推送到 JDBC 驱动程序的时区。 请参阅 quarkus.hibernate-orm.mapping.timezone.default-storage

环境变量:QUARKUS_HIBERNATE_ORM_JDBC_TIMEZONE

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".jdbc.statement-fetch-size

JDBC 驱动程序一次提取多少行。

环境变量:QUARKUS_HIBERNATE_ORM_JDBC_STATEMENT_FETCH_SIZE

显示更多

整数

quarkus.hibernate-orm."持久化单元名称".jdbc.statement-batch-size

JDBC 驱动程序一次发送多少更新(插入、更新和删除)来执行。

环境变量:QUARKUS_HIBERNATE_ORM_JDBC_STATEMENT_BATCH_SIZE

显示更多

整数

提取逻辑配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".fetch.batch-size

加载实体和集合时使用的批次的大小。

-1 表示禁用批处理加载。

环境变量:QUARKUS_HIBERNATE_ORM_FETCH_BATCH_SIZE

显示更多

整数

16

quarkus.hibernate-orm."持久化单元名称".fetch.max-depth

单端关联(一对一,多对一)的外连接提取树的最大深度。

0 将禁用默认的外部连接抓取。

环境变量:QUARKUS_HIBERNATE_ORM_FETCH_MAX_DEPTH

显示更多

整数

缓存配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".cache."cache".expiration.max-idle

缓存对象被视为过期的最大时间。

环境变量:QUARKUS_HIBERNATE_ORM_CACHE__CACHE__EXPIRATION_MAX_IDLE

显示更多

Duration 

quarkus.hibernate-orm."持久化单元名称".cache."cache".memory.object-count

缓存中内存中保存的最大对象数。

环境变量:QUARKUS_HIBERNATE_ORM_CACHE__CACHE__MEMORY_OBJECT_COUNT

显示更多

long

鉴别器相关配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".discriminator.ignore-explicit-for-joined

现有应用程序依赖(隐式或显式)Hibernate 忽略连接继承层次结构上的任何 DiscriminatorColumn 声明。此设置允许这些应用程序维护 DiscriminatorColumn 注解在与连接继承配对时被忽略的传统行为。

环境变量:QUARKUS_HIBERNATE_ORM_DISCRIMINATOR_IGNORE_EXPLICIT_FOR_JOINED

显示更多

布尔值

false

日志配置

类型

默认

记录 SQL 绑定参数。

显然,不建议在生产环境中将其设置为 true。

环境变量:QUARKUS_HIBERNATE_ORM_LOG_BIND_PARAMETERS

显示更多

布尔值

false

quarkus.hibernate-orm."持久化单元名称".log.sql

显示 SQL 日志并很好地格式化它们。

显然,不建议在生产环境中将其设置为 true。

环境变量:QUARKUS_HIBERNATE_ORM_LOG_SQL

显示更多

布尔值

false

quarkus.hibernate-orm."持久化单元名称".log.format-sql

如果启用了 SQL 日志,则格式化 SQL 日志

环境变量:QUARKUS_HIBERNATE_ORM_LOG_FORMAT_SQL

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".log.highlight-sql

如果启用了 SQL 日志,则高亮显示 SQL 日志

环境变量:QUARKUS_HIBERNATE_ORM_LOG_HIGHLIGHT_SQL

显示更多

布尔值

true

quarkus.hibernate-orm."持久化单元名称".log.jdbc-warnings

是否应该收集和记录 JDBC 警告。

环境变量:QUARKUS_HIBERNATE_ORM_LOG_JDBC_WARNINGS

显示更多

布尔值

取决于方言

quarkus.hibernate-orm."持久化单元名称".log.queries-slower-than-ms

如果设置,Hibernate 将记录执行时间超过指定毫秒数的查询。

环境变量:QUARKUS_HIBERNATE_ORM_LOG_QUERIES_SLOWER_THAN_MS

显示更多

long

模式管理配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".schema-management.strategy

选择是否生成数据库模式。

drop-and-create 在开发模式下非常棒。

默认为“none”。

但是,如果 Dev Services 正在使用中,并且不存在其他管理模式的扩展,则该值将自动覆盖为“drop-and-create”。

可接受的值:nonecreatedrop-and-createdropupdatevalidate

环境变量:QUARKUS_HIBERNATE_ORM_SCHEMA_MANAGEMENT_STRATEGY

显示更多

字符串

none

quarkus.hibernate-orm."持久化单元名称".schema-management.create-schemas

如果 Hibernate ORM 应该自动创建模式(对于支持它们的数据库)。

环境变量:QUARKUS_HIBERNATE_ORM_SCHEMA_MANAGEMENT_CREATE_SCHEMAS

显示更多

布尔值

false

quarkus.hibernate-orm."持久化单元名称".schema-management.halt-on-error

在应用模式时,我们是否应该在第一个错误时停止。

环境变量:QUARKUS_HIBERNATE_ORM_SCHEMA_MANAGEMENT_HALT_ON_ERROR

显示更多

布尔值

false

数据库脚本相关配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".scripts.generation

选择是否生成数据库模式 DDL 文件。可接受的值:nonecreatedrop-and-createdropupdatevalidate

环境变量:QUARKUS_HIBERNATE_ORM_SCRIPTS_GENERATION

显示更多

字符串

none

quarkus.hibernate-orm."持久化单元名称".scripts.generation.create-target

应该在其中生成数据库创建 DDL 文件的文件名或 URL。

环境变量:QUARKUS_HIBERNATE_ORM_SCRIPTS_GENERATION_CREATE_TARGET

显示更多

字符串

quarkus.hibernate-orm."持久化单元名称".scripts.generation.drop-target

应该在其中生成数据库删除 DDL 文件的文件名或 URL。

环境变量:QUARKUS_HIBERNATE_ORM_SCRIPTS_GENERATION_DROP_TARGET

显示更多

字符串

刷新配置

类型

默认

quarkus.hibernate-orm."持久化单元名称".flush.mode

默认的刷新策略,或何时将实体刷新到 Hibernate 会话中的数据库:在每次查询之前,在提交时……

可以通过 Session#setHibernateFlushMode() 在每个会话的基础上覆盖此默认设置,或者使用提示 HibernateHints#HINT_FLUSH_MODE 在每个查询的基础上覆盖此默认设置。

有关详细信息,请参阅 org.hibernate.FlushMode 的 javadoc。

环境变量:QUARKUS_HIBERNATE_ORM_FLUSH_MODE

显示更多

manualcommitautoalways

auto

关于 Duration 格式

要写入持续时间值,请使用标准的 java.time.Duration 格式。有关更多信息,请参阅Duration#parse() Java API 文档

您还可以使用简化的格式,以数字开头

  • 如果该值仅为一个数字,则表示以秒为单位的时间。

  • 如果该值是一个数字后跟 ms,则表示以毫秒为单位的时间。

在其他情况下,简化格式将被转换为 java.time.Duration 格式以进行解析

  • 如果该值是一个数字后跟 hms,则在其前面加上 PT

  • 如果该值是一个数字后跟 d,则在其前面加上 P

不要在 application.properties 中混合使用 persistence.xmlquarkus.hibernate-orm.* 属性。Quarkus 将引发异常。确定您要使用哪种方法。

如果您希望忽略类路径中的 persistence.xml,请设置以下配置属性

quarkus.hibernate-orm.persistence-xml.ignore=true

想用 Docker 在旁边启动一个 PostgreSQL 服务器吗?

docker run --rm=true --name postgres-quarkus-hibernate -e POSTGRES_USER=hibernate \
           -e POSTGRES_PASSWORD=hibernate -e POSTGRES_DB=hibernate_db \
           -p 5432:5432 postgres:14.1

这将启动一个非持久化的空数据库:非常适合快速实验!

多个持久化单元

设置多个持久化单元

可以使用 Quarkus 配置属性定义多个持久化单元。

quarkus.hibernate-orm. 命名空间根目录下的属性定义了默认持久化单元。例如,以下代码片段定义了一个默认数据源和一个默认持久化单元

quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1

quarkus.hibernate-orm.schema-management.strategy=drop-and-create

通过基于映射的方法,可以定义命名的持久化单元

quarkus.datasource."users".db-kind=h2 (1)
quarkus.datasource."users".jdbc.url=jdbc:h2:mem:users;DB_CLOSE_DELAY=-1

quarkus.datasource."inventory".db-kind=h2 (2)
quarkus.datasource."inventory".jdbc.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=-1

quarkus.hibernate-orm."users".schema-management.strategy=drop-and-create (3)
quarkus.hibernate-orm."users".datasource=users (4)
quarkus.hibernate-orm."users".packages=org.acme.model.user (5)

quarkus.hibernate-orm."inventory".schema-management.strategy=drop-and-create (6)
quarkus.hibernate-orm."inventory".datasource=inventory
quarkus.hibernate-orm."inventory".packages=org.acme.model.inventory
1 定义一个名为 users 的数据源。
2 定义一个名为 inventory 的数据源。
3 定义一个名为 users 的持久化单元。
4 定义持久化单元使用的数据源。
5 此配置属性很重要,但我们稍后会讨论。
6 定义一个名为 inventory 的持久化单元,指向 inventory 数据源。

您可以混合使用默认数据源和命名数据源,或者只使用其中一个。

默认持久化单元默认指向默认数据源。对于命名持久化单元,datasource 属性是必需的。您可以通过将其设置为 <default>(这是默认数据源的内部名称)来将持久化单元指向默认数据源。

让多个持久化单元指向同一个数据源是完全有效的。

将模型类附加到持久化单元

有两种方法可以将模型类附加到持久化单元,并且不应混合使用

  • 通过 packages 配置属性;

  • 通过 @io.quarkus.hibernate.orm.PersistenceUnit 包级别注解。

如果两者混合使用,注解将被忽略,并且只考虑 packages 配置属性。

使用 packages 配置属性很简单

quarkus.hibernate-orm.schema-management.strategy=drop-and-create
quarkus.hibernate-orm.packages=org.acme.model.defaultpu

quarkus.hibernate-orm."users".schema-management.strategy=drop-and-create
quarkus.hibernate-orm."users".datasource=users
quarkus.hibernate-orm."users".packages=org.acme.model.user

此配置片段将创建两个持久化单元

  • 默认持久化单元,它将包含 org.acme.model.defaultpu 包下的所有模型类,包括子包。

  • 一个名为 users 的持久化单元,它将包含 org.acme.model.user 包下的所有模型类,包括子包。

您可以将多个包附加到持久化单元

quarkus.hibernate-orm."users".packages=org.acme.model.shared,org.acme.model.user

org.acme.model.sharedorg.acme.model.user 包下的所有模型类都将附加到 users 持久化单元。

也支持将给定的模型类附加到多个持久化单元。

模型类需要一致地添加到给定的持久化单元。这意味着给定实体的所有依赖模型类(映射的超类、可嵌入类等)都必须附加到持久化单元。由于我们在包级别处理持久化单元,因此这应该足够简单。

Panache 实体只能附加到一个持久化单元。

对于附加到多个持久化单元的实体,您不能使用 Panache。但是,您可以混合使用这两种方法,并在需要多个持久化单元时混合使用 Panache 实体和传统实体。

如果您有此用例,并且有关于如何实现它的聪明想法,而不会弄乱简化的 Panache 方法,请通过quarkus-dev 邮件列表与我们联系。

将模型类附加到持久化单元的第二种方法是使用包级别的 @io.quarkus.hibernate.orm.PersistenceUnit 注解。同样,这两种方法不能混合使用。

要获得与使用 packages 配置属性的配置类似的效果,请创建一个 package-info.java 文件,内容如下

@PersistenceUnit("users") (1)
package org.acme.model.user;

import io.quarkus.hibernate.orm.PersistenceUnit;
1 请注意,使用 @io.quarkus.hibernate.orm.PersistenceUnit 注解,而不是 Jakarta Persistence 注解。

在此情况下,我们仅支持在包级别定义模型类的 @PersistenceUnit,不支持在类级别使用 @PersistenceUnit 注解。

请注意,与我们使用配置属性的方式类似,我们不仅考虑了注解的包,还考虑了其所有子包。

CDI 集成

如果您熟悉在 Quarkus 中使用 Hibernate ORM,您可能已经使用 CDI 注入了 EntityManager

@Inject
EntityManager entityManager;

这将注入默认持久化单元的 EntityManager

注入命名持久化单元(例如我们的 users)的 EntityManager 非常简单,如下所示

@Inject
@PersistenceUnit("users") (1)
EntityManager entityManager;
1 这里,我们再次使用相同的 @io.quarkus.hibernate.orm.PersistenceUnit 注解。

使用相同的机制可以注入命名持久化单元的 EntityManagerFactory

@Inject
@PersistenceUnit("users")
EntityManagerFactory entityManagerFactory;

除了 EntityManagerEntityManagerFactory 之外,Quarkus 还支持注入以下 JPA/Hibernate 组件

@Inject
CriteriaBuilder criteriaBuilder;

@Inject
HibernateCriteriaBuilder hibernateCriteriaBuilder;

@Inject
Metamodel metamodel;

@Inject
jakarta.persistence.Cache cache;

@Inject
org.hibernate.Cache cache;

@Inject
jakarta.persistence.PersistenceUnitUtil persistenceUnitUtil;

@Inject
jakarta.persistence.SchemaManager schemaManager;

@Inject
org.hibernate.relational.SchemaManager schemaManager;

这些组件也可以使用特定的持久化单元限定符进行注入

@Inject
@PersistenceUnit("users")
CriteriaBuilder criteriaBuilder;

激活/停用持久化单元

如果在构建时配置了持久化单元,则默认情况下它在运行时是激活的,即 Quarkus 将在应用程序启动时启动相应的 Hibernate ORM SessionFactory

要在运行时停用持久化单元,请将 quarkus.hibernate-orm[.optional name].active 设置为 false。如果持久化单元未激活

  • SessionFactory 将不会在应用程序启动期间启动。

  • 访问 EntityManagerFactory/EntityManagerSessionFactory/Session 将导致抛出异常。

这尤其有用,当您希望应用程序能够在运行时使用预定数据集中的一个数据源

例如,使用以下配置

quarkus.hibernate-orm."pg".packages=org.acme.model.shared
quarkus.hibernate-orm."pg".datasource=pg
quarkus.hibernate-orm."pg".schema-management.strategy=drop-and-create
quarkus.hibernate-orm."pg".active=false
quarkus.datasource."pg".db-kind=h2
quarkus.datasource."pg".active=false
%prod.quarkus.datasource."pg".jdbc.url=jdbc:postgresql:///your_database

quarkus.hibernate-orm."oracle".packages=org.acme.model.shared
quarkus.hibernate-orm."oracle".datasource=oracle
quarkus.hibernate-orm."oracle".schema-management.strategy=drop-and-create
quarkus.hibernate-orm."oracle".active=false
quarkus.datasource."oracle".db-kind=oracle
quarkus.datasource."oracle".active=false
%prod.quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database

在运行时设置 quarkus.hibernate-orm."pg".active=truequarkus.datasource."pg".active=true 将仅使 PostgreSQL 持久化单元和数据源可用,而在运行时设置 quarkus.hibernate-orm."oracle".active=truequarkus.datasource."oracle".active=true 将仅使 Oracle 持久化单元和数据源可用。

可以使用自定义配置配置文件来简化此类设置。通过将以下特定于配置文件的配置添加到上述配置中,您可以通过设置 quarkus.profile 在运行时选择持久化单元/数据源:quarkus.profile=prod,pgquarkus.profile=prod,oracle

%pg.quarkus.hibernate-orm."pg".active=true
%pg.quarkus.datasource."pg".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."

%oracle.quarkus.hibernate-orm."oracle".active=true
%oracle.quarkus.datasource."oracle".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."

有了这样的设置,您需要注意只访问*活动的*持久化单元。为此,您可以为默认 Session 定义一个CDI bean producer,将其实例重定向到当前活动的命名 Session,以便可以直接注入它,如下所示

public class MyProducer {
    @Inject
    @DataSource("pg")
    InjectableInstance<AgroalDataSource> pgDataSourceBean; (1)

    @Inject
    @DataSource("oracle")
    InjectableInstance<AgroalDataSource> oracleDataSourceBean;

    @Inject
    @PersistenceUnit("pg")
    Session pgSessionBean;

    @Inject
    @PersistenceUnit("oracle")
    Session oracleSessionBean;

    @Produces (2)
    @ApplicationScoped
    public Session session() {
        if (pgDataSourceBean.getHandle().getBean().isActive()) { (3)
            return pgSessionBean;
        } else if (oracleDataSourceBean.getHandle().getBean().isActive()) { (3)
            return oracleSessionBean;
        } else {
            throw new RuntimeException("No active datasource!");
        }
    }
}

@ApplicationScoped
public class MyConsumer {
    @Inject
    Session session; (4)

    public void doSomething() {
        // .. just use the injected session ...
    }
}
1 不要直接注入 DataSourceAgroalDatasource,因为这会导致启动失败(无法注入非活动 bean)。相反,注入 InjectableInstance<DataSource>InjectableInstance<AgroalDataSource>
2 声明一个 CDI producer 方法,该方法将默认会话定义为 PostgreSQL 或 Oracle,具体取决于哪个是活动的。
3 在检索相应的会话之前,检查数据源 bean 是否处于活动状态。
4 这将注入(唯一)活动的会话。

使用 persistence.xml 设置和配置 Hibernate ORM

要设置和配置 Hibernate ORM,推荐使用 application.properties,但您也可以选择使用 META-INF/persistence.xml 文件。这主要用于将现有代码迁移到 Quarkus。

使用 persistence.xml 文件会带来一些限制

  • persistence.xml 中定义的持久化单元始终使用默认数据源

  • persistence.xml 中定义的持久化单元必须显式配置:Quarkus 将将与环境相关的配置注入降至最低。

    特别是,Quarkus 不会根据数据源自动配置方言或数据库版本,因此如果 Hibernate ORM 的默认配置不适合您的需求,您需要在 persistence.xml 配置中包含hibernate.dialect/jakarta.persistence.database-product-name 以及可能的jakarta.persistence.database-product-version

  • 使用 persistence.xml 与在 application.properties 中使用 quarkus.hibernate-orm.* 属性不兼容:如果您混合使用它们,Quarkus 将引发异常。

  • 与使用application.properties 相比,使用 persistence.xml 可能会对开发者体验产生负面影响,因为缺少功能、Quarkus 文档中的指导有限以及错误消息提供的解析提示无法应用(例如,使用 quarkus.hibernate-orm.* 属性)。

如果您希望忽略类路径中的 persistence.xml,请设置以下配置属性

quarkus.hibernate-orm.persistence-xml.ignore=true

您的 pom.xml 依赖项以及您的 Java 代码将与之前的示例相同。唯一的区别是您将在 META-INF/persistence.xml 中指定 Hibernate ORM 配置

示例 persistence.xml 资源
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="CustomerPU" transaction-type="JTA">

        <description>My customer entities</description>

        <properties>
            <!-- Connection specific -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>

            <!--
                Optimistically create the tables;
                will cause background errors being logged if they already exist,
                but is practical to retain existing data across runs (or create as needed) -->
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>

            <property name="jakarta.persistence.validation.mode" value="NONE"/>
        </properties>

    </persistence-unit>
</persistence>

使用 persistence.xml 配置时,您直接配置 Hibernate ORM,因此在这种情况下,适当的参考是hibernate.org 上的文档

请记住,这些属性名与 Quarkus application.properties 中使用的属性名不同,默认值也不会相同。

XML 映射

Quarkus 中的 Hibernate ORM 支持 XML 映射。您可以添加遵循orm.xml 格式(Jakarta Persistence)hbm.xml 格式(Hibernate ORM 特有,已弃用)的映射文件

XML 映射文件在构建时解析。

如果 META-INF/orm.xml 文件存在于类路径中,则默认情况下始终包含该文件。

如果这不是您想要的,请使用 quarkus.hibernate-orm.mapping-files = no-file<mapping-file>no-file</mapping-file>

在外部项目或 jar 中定义实体

Quarkus 中的 Hibernate ORM 依赖于编译时字节码增强您的实体。如果您在构建 Quarkus 应用程序的同一个项目中定义实体,一切都会正常工作。

如果实体来自外部项目或 jar,您可以通过添加一个空的 META-INF/beans.xml 文件来确保您的 jar 被视为 Quarkus 应用程序库。

这将允许 Quarkus 索引和增强您的实体,就像它们位于当前项目中一样。

开发模式下的 Hibernate ORM

Quarkus 开发模式对于混合前端或服务和数据库访问的应用程序非常有用。

有几种常用方法可以充分利用它。

第一个选择是将 quarkus.hibernate-orm.schema-management.strategy=drop-and-createimport.sql 结合使用。

这样,对于您应用程序的每一次更改,特别是对您的实体的更改,数据库模式都将被正确地重新创建,并且您的数据夹具(存储在 import.sql 中)将用于从头开始重新填充它。这对于完美控制您的环境是最好的,并且与 Quarkus 实时重新加载模式配合得很好:您的实体更改或 import.sql 的任何更改都会立即被拾取,并且在不重启应用程序的情况下更新模式!

默认情况下,在 devtest 模式下,Hibernate ORM 在启动时会读取并执行 /import.sql 文件(如果存在)中的 SQL 语句。您可以通过在 application.properties 中更改 quarkus.hibernate-orm.sql-load-script 属性来更改文件名。

第二个方法是使用 quarkus.hibernate-orm.schema-management.strategy=update。此方法最适合您进行许多实体更改,但仍需要处理生产数据的副本,或者如果您想重现基于特定数据库条目的错误。update 是 Hibernate ORM 的尽力而为,在特定情况下会失败,包括更改可能导致数据丢失的数据库结构。例如,如果您更改违反外键约束的结构,Hibernate ORM 可能不得不放弃。但对于开发来说,这些限制是可以接受的。

第三种方法是使用 quarkus.hibernate-orm.schema-management.strategy=none。此方法最适合您处理生产数据的副本,但又想完全控制模式演变。或者,如果您使用数据库模式迁移工具,如FlywayLiquibase

使用此方法进行实体更改时,请确保相应地调整数据库模式;您也可以使用 validate 让 Hibernate 验证模式是否符合其预期。

不要在生产环境中使用 quarkus.hibernate-orm.schema-management.strategy drop-and-createupdate

当与 Quarkus 配置配置文件结合使用时,这些方法会变得非常强大。您可以定义不同的配置配置文件,根据您的环境选择不同的行为。这很棒,因为您可以定义 Hibernate ORM 属性的不同组合,以匹配您当前所需的开发风格。

application.properties
%dev.quarkus.hibernate-orm.schema-management.strategy = drop-and-create
%dev.quarkus.hibernate-orm.sql-load-script = import-dev.sql

%dev-with-data.quarkus.hibernate-orm.schema-management.strategy = update
%dev-with-data.quarkus.hibernate-orm.sql-load-script = no-file

%prod.quarkus.hibernate-orm.schema-management.strategy = none
%prod.quarkus.hibernate-orm.sql-load-script = no-file

您可以使用自定义配置文件启动开发模式

CLI
quarkus dev -Dquarkus.profile=dev-with-data
Maven
./mvnw quarkus:dev -Dquarkus.profile=dev-with-data
Gradle
./gradlew --console=plain quarkusDev -Dquarkus.profile=dev-with-data

生产模式下的 Hibernate ORM

Quarkus 带有默认配置文件(devtestprod)。您可以添加自己的自定义配置文件来描述各种环境(stagingprod-us 等)。

Hibernate ORM Quarkus 扩展在 dev 和 test 模式下的默认配置与其他环境不同。

  • quarkus.hibernate-orm.sql-load-scriptdevtest 配置文件外,均设置为 no-file

您可以在 application.properties 中显式覆盖它(例如,%prod.quarkus.hibernate-orm.sql-load-script = import.sql),但我们希望您避免在 prod 中意外覆盖数据库 :)

说起来,请确保不要在生产环境中删除数据库模式!在您的属性文件中添加以下内容。

application.properties
%prod.quarkus.hibernate-orm.schema-management.strategy = none
%prod.quarkus.hibernate-orm.sql-load-script = no-file

自动转换为 Flyway 管理模式

如果您在开发模式下安装了Flyway 扩展,Quarkus 提供了一种简单的方法来使用 Hibernate ORM 自动生成的模式来初始化您的 Flyway 配置。这旨在简化从早期开发阶段的迁移,在早期开发阶段,Hibernate 可用于快速设置模式,而在生产阶段,Flyway 用于管理模式更改。

要使用此功能,只需打开 Dev UI,当安装了 quarkus-flyway 扩展时,然后在 Flyway 窗格中的 Datasources 链接上单击。点击 Create Initial Migration 按钮,然后会发生以下情况

  • 将创建一个 db/migration/V1.0.0__{appname}.sql 文件,其中包含 Hibernate 用于生成模式的 SQL

  • 将设置 quarkus.flyway.baseline-on-migrate,告知 Flyway 自动创建其基线表

  • 将设置 quarkus.flyway.migrate-at-start,告知 Flyway 在应用程序启动时自动应用迁移

  • 将在 %dev.quarkus.flyway.clean-at-start%test.quarkus.flyway.clean-at-start 设置,以便在 dev/test 模式下重新加载后清理 DB

此按钮仅用于方便地让您快速开始使用 Flyway,如何管理生产环境中的数据库模式由您决定。特别是 migrate-at-start 设置可能不适用于所有环境。

缓存

当 Hibernate ORM 二级缓存启用时,频繁读取相同实体的应用程序的性能会得到提高。

实体缓存

要启用二级缓存,请使用 @jakarta.persistence.Cacheable 注解您要缓存的实体

@Entity
@Cacheable
public class Country {
    int dialInCode;
    // ...
}

当实体被注解为 @Cacheable 时,其所有字段值都会被缓存,但集合和与其他实体的关系除外。

这意味着可以在不查询数据库的情况下加载实体,但请注意,这可能意味着加载的实体可能无法反映数据库中的最新更改。

集合和关系缓存

集合和关系需要单独注解才能进行缓存;在这种情况下,应使用 Hibernate 特有的 @org.hibernate.annotations.Cache,这还需要指定 CacheConcurrencyStrategy

package org.acme;

@Entity
@Cacheable
public class Country {
    // ...

    @OneToMany
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    List<City> cities;

    // ...
}

查询缓存

查询也可以从二级缓存中受益。缓存的查询结果可以立即返回给调用者,避免在数据库上运行查询。

请注意,这可能意味着结果可能无法反映最新更改。

要缓存查询,请在 Query 实例上将其标记为可缓存

Query query = ...
query.setHint("org.hibernate.cacheable", Boolean.TRUE);

如果您有 NamedQuery,则可以直接在其定义上启用缓存,通常是在实体上

@Entity
@NamedQuery(name = "Fruits.findAll",
      query = "SELECT f FROM Fruit f ORDER BY f.name",
      hints = @QueryHint(name = "org.hibernate.cacheable", value = "true") )
public class Fruit {
   ...

就是这样!缓存技术已在 Quarkus 中集成并默认启用,因此只需设置哪些内容可以安全缓存即可。

缓存区域调整

缓存将数据存储在单独的区域中,以隔离不同部分的数据;这些区域被分配了一个名称,这对于独立配置每个区域或监控其统计信息很有用。

默认情况下,实体缓存在以其完全限定名命名的区域中,例如 org.acme.Country

集合缓存在以其所有者实体和集合字段名的完全限定名命名的区域中,用 # 字符分隔,例如 org.acme.Country#cities

所有缓存的查询默认都保存在一个专用于它们的区域中,称为 default-query-results-region

所有区域默认都受大小和时间限制。默认值为 10000 个最大条目,以及 100 秒的最大空闲时间。

每个区域的大小可以通过 quarkus.hibernate-orm.cache."<region_name>".memory.object-count 属性进行自定义(将 <region_name> 替换为实际区域名称)。

要设置最大空闲时间,请通过 quarkus.hibernate-orm.cache."<region_name>".expiration.max-idle 属性提供持续时间(请参阅下方关于持续时间格式的注释)(将 <region_name> 替换为实际区域名称)。

如果您的区域名称包含点,则双引号是必需的。例如

quarkus.hibernate-orm.cache."org.acme.MyEntity".memory.object-count=1000

要写入持续时间值,请使用标准的 java.time.Duration 格式。有关更多信息,请参阅Duration#parse() javadoc

您还可以使用简化的格式,以数字开头

  • 如果该值仅为一个数字,则表示以秒为单位的时间。

  • 如果该值是一个数字后跟 ms,则表示以毫秒为单位的时间。

在其他情况下,简化格式将被转换为 java.time.Duration 格式以进行解析

  • 如果该值是一个数字后跟 hms,则在其前面加上 PT

  • 如果该值是一个数字后跟 d,则在其前面加上 P

缓存限制

Quarkus 中提供的缓存技术目前相当粗糙和有限。

团队认为,最好先拥有*一些*缓存功能,而不是什么都没有;您可以预期在未来的版本中会集成更好的缓存解决方案,并且非常欢迎在此领域提供的任何帮助和反馈。

这些缓存保存在本地,因此当其他应用程序对持久化存储进行更改时,它们不会失效或更新。

此外,当同一应用程序运行多个副本时(例如在 Kubernetes/OpenShift 上集群),不同副本中的缓存不同步。

出于这些原因,启用缓存仅适用于可以做出某些假设的情况:我们强烈建议只缓存永远不会更改的实体、集合和查询。或者最多,当确实发生了对该实体的修改并且允许读取过时的(陈旧的)数据时,这对应用程序的预期没有影响。

遵循此建议可以确保应用程序从二级缓存获得最佳性能,同时避免意外行为。

除了不可变数据之外,在某些上下文中,在可变数据上启用缓存也是可以接受的;这可能是对经常读取并且可以容忍一定程度陈旧性的选定实体进行必要权衡的;这可以通过设置驱逐属性来调整。然而,不建议这样做,并且应极其谨慎地进行,因为它可能会产生意外和不可预见的数据效果。

理想情况下,与其在可变数据上启用缓存,不如使用集群缓存;然而,目前 Quarkus 不提供任何此类实现:请随时联系我们,告知此需求,以便团队能够将其考虑在内。

最后,可以通过将 hibernate.cache.use_second_level_cache 设置为 false 来全局禁用二级缓存;这是一个需要在 persistence.xml 配置文件中指定的设置。

当禁用二级缓存时,所有缓存注解都将被忽略,并且所有查询都将在忽略缓存的情况下运行;这通常只在诊断问题时有用。

Hibernate Envers

Hibernate ORM 的 Envers 扩展旨在为实体类提供简单的审计/版本控制解决方案。

在 Quarkus 中,Envers 有一个专用的 Quarkus 扩展 io.quarkus:quarkus-hibernate-envers;您只需将其添加到项目中即可开始使用。

启用 Hibernate Envers 的附加依赖
    <!-- Add the Hibernate Envers extension -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-envers</artifactId>
    </dependency>

此时,该扩展不公开其他配置属性。

有关 Hibernate Envers 的更多信息,请参阅hibernate.org/orm/envers/

指标

Micrometer 或 SmallRye Metrics 能够公开 Hibernate ORM 在运行时收集的指标。要启用 Hibernate 指标在 /q/metrics 端点上的公开,请确保您的项目依赖于指标扩展,并将配置属性 quarkus.hibernate-orm.metrics.enabled 设置为 true。使用SmallRye Metrics时,指标将在 vendor 作用域下可用。

局限性和其他您应该知道的事情

Quarkus 不会修改它使用的库;此规则也适用于 Hibernate ORM:使用此扩展时,您的体验与使用原始库大致相同。

但是,虽然它们共享相同的代码,但 Quarkus 会自动配置某些组件,并为某些扩展点注入自定义实现;这应该是透明且有用的,但如果您是 Hibernate 的专家,您可能想知道正在做什么。

自动构建时增强

Hibernate ORM 可以使用构建时增强的实体;通常这不是必需的,但它很有用,并且可以提高应用程序的性能。

通常,您需要调整构建脚本以包含 Hibernate Enhancement 插件;在 Quarkus 中,这不是必需的,因为增强步骤已集成到 Quarkus 应用程序的构建和分析中。

由于使用了增强,因此目前不支持在实体上使用 clone() 方法,因为它还会克隆一些特定于实体的特定于增强的字段。

此限制将来可能会被移除。

自动集成

事务管理器集成

您无需设置此项,Quarkus 会自动注入 Narayana 事务管理器的引用。依赖项作为 Hibernate ORM 扩展的传递依赖项自动包含。所有配置都是可选的;有关更多详细信息,请参阅在 Quarkus 中使用事务

连接池

您也不需要选择一个。Quarkus 会自动包含 Agroal 连接池;如以上示例所示配置您的数据源,它将设置 Hibernate ORM 以使用 Agroal。有关此连接池的更多详细信息,请参阅Quarkus - Datasources

二级缓存

缓存部分中所述,您无需选择实现。基于InfinispanCaffeine 的技术的合适实现作为 Hibernate ORM 扩展的传递依赖项包含,并在构建期间自动集成。

限制

XML 映射与类路径中的重复文件

XML 映射文件的路径应该是唯一的。

实际上,在非常特定的场景中,类路径中只能出现重复的 XML 映射文件。例如,如果两个 JAR 文件包含 META-INF/orm.xml 文件(路径完全相同,但在不同的 JAR 文件中),那么映射文件路径 META-INF/orm.xml 只能从位于*与 META-INF/orm.xml 文件相同的 JAR* 中的 persistence.xml 引用。

JMX

JMX 管理 Bean 在 GraalVM 原生映像中不起作用;因此,当编译为原生映像时,Hibernate 将统计信息和管理操作注册到 JMX Bean 的功能将被禁用。此限制可能是永久性的,因为它不是原生映像支持 JMX 的目标。所有这些指标都可以通过其他方式访问。

JACC 集成

Hibernate ORM 与 JACC 集成的能力在构建 GraalVM 原生映像时被禁用,因为 JACC 在原生模式下不可用,也无用。

将会话绑定到 ThreadLocal 上下文

不可能使用 Hibernate ORM 的 ThreadLocalSessionContext 助手,因为不支持它。由于 Quarkus 提供开箱即用的 CDI 支持,因此注入或编程 CDI 查找是更好的方法。此功能也与响应式组件和更现代的上下文传播技术集成不佳,这使我们相信此遗留功能没有未来。如果您非常需要将其绑定到 ThreadLocal,则在您自己的代码中实现它应该很简单。

JNDI

JNDI 技术通常在其他运行时中用于集成不同的组件。一个常见的用例是 Java Enterprise 服务器将 TransactionManager 和 Datasource 组件绑定到一个名称,然后配置 Hibernate ORM 以按名称查找这些组件。但在 Quarkus 中,这种情况不适用,因为组件是直接注入的,这使得 JNDI 支持成为不必要的遗留。为避免意外使用 JNDI,Hibernate ORM 扩展对 Quarkus 的 JNDI 支持已被完全禁用。这既是为了安全考虑,也是为了优化。

其他值得注意的区别

import.sql 的格式

当导入 import.sql 设置数据库时,请记住 Quarkus 会重新配置 Hibernate ORM,以要求使用分号(;)来终止每个语句。Hibernate 的默认设置是每行一个语句,不需要换行符以外的终止符:如果您重用现有脚本,请记住将脚本转换为使用 ; 终止符字符。这很有用,可以允许多行语句和人类可读的格式。

使用 Panache 简化 Hibernate ORM

Hibernate ORM with Panache 扩展通过提供 ActiveRecord 风格的实体(和存储库)来简化 Hibernate ORM 的使用,并专注于使您的实体在 Quarkus 中编写起来简单有趣。

配置您的数据源

数据源配置非常简单,但已在不同的指南中介绍,因为它在技术上是由 Quarkus 的 Agroal 连接池扩展实现的。

请转到Quarkus - Datasources 以获取所有详细信息。

多租户

“总的来说,多租户这个词被应用于软件开发,以表示一种架构,在这种架构中,应用程序的单个运行实例同时服务于多个客户(租户)。这在 SaaS 解决方案中非常普遍。隔离与各个租户相关的信息(数据、自定义等)是这些系统中的一个特定挑战。这包括存储在数据库中的每个租户拥有的数据”(Hibernate 用户指南)。

Quarkus 目前支持独立数据库方法、独立模式方法和鉴别器方法。

编写应用程序

让我们开始实现 /{tenant} 端点。从下面的源代码可以看出,它只是一个常规的 Jakarta REST 资源

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@ApplicationScoped
@Path("/{tenant}")
public class FruitResource {

    @Inject
    EntityManager entityManager;

    @GET
    @Path("fruits")
    public Fruit[] getFruits() {
        return entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
                .getResultList().toArray(new Fruit[0]);
    }

}

为了从传入请求解析租户并将其映射到特定的租户配置,您需要为 io.quarkus.hibernate.orm.runtime.tenant.TenantResolver 接口创建一个实现。

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
import io.vertx.ext.web.RoutingContext;

@PersistenceUnitExtension (1)
@RequestScoped (2)
public class CustomTenantResolver implements TenantResolver {

    @Inject
    RoutingContext context;

    @Override
    public String getDefaultTenantId() {
        return "base";
    }

    @Override
    public String resolveTenantId() {
        String path = context.request().path();
        String[] parts = path.split("/");

        if (parts.length == 0) {
            // resolve to default tenant config
            return getDefaultTenantId();
        }

        return parts[1];
    }

}
1 使用 @PersistenceUnitExtension 限定符注解 TenantResolver 实现,以告知 Quarkus 它应该在默认持久化单元中使用。

对于命名持久化单元,请使用 @PersistenceUnitExtension("nameOfYourPU")

2 该 bean 被设置为 @RequestScoped,因为租户解析取决于传入的请求。

从上面的实现可以看出,租户是从请求路径解析的,因此在无法推断出租户的情况下,将返回默认租户标识符。

如果您还使用OIDC 多租户,并且 OIDC 和 Hibernate ORM 租户 ID 相同,您可以从 RoutingContext 属性中获取 OIDC 租户 ID,如下例所示

import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
import io.quarkus.oidc.runtime.OidcUtils;
import io.vertx.ext.web.RoutingContext;

@PersistenceUnitExtension
@RequestScoped
public class CustomTenantResolver implements TenantResolver {

    @Inject
    RoutingContext context; (1)
    ...
    @Override
    public String resolveTenantId() {
        // OIDC has saved the tenant id as the RoutingContext attribute:
        return context.get(OidcUtils.TENANT_ID_ATTRIBUTE);
    }
}
1 如果 Hibernate TenantResolver 在没有活动 HTTP 请求时也解析租户,例如当您从调度程序查询数据库时,RoutingContext CDI bean 将不可用。请改用注入 io.quarkus.vertx.http.runtime.CurrentVertxRequest CDI bean,并从该 bean 中获取 RoutingContext

配置应用程序

通常,无法在多租户设置中同时使用 Hibernate ORM 数据库生成功能。因此,您必须禁用它,并且需要确保为每个模式创建表。以下设置将使用Flyway 扩展来实现此目标。

SCHEMA 方法

所有租户将使用相同的数据源,并且必须在每个数据源中为每个租户创建一个模式。

某些数据库(如 MariaDB/MySQL)默认不支持数据库模式。在这种情况下,您可以:1. 配置 JDBC 驱动程序以支持模式。使用 quarkus.datasource.jdbc.additional-jdbc-properties."databaseTerm"=SCHEMAquarkus.datasource."datasource-name".jdbc.additional-jdbc-properties."databaseTerm"=SCHEMA 用于MySQL Connector/J。使用 quarkus.datasource.jdbc.additional-jdbc-properties."useCatalogTerm"=SCHEMAquarkus.datasource."datasource-name".jdbc.additional-jdbc-properties."useCatalogTerm"=SCHEMA 用于MariaDB Connector/J

并 2. 回退到数据库方法

quarkus.hibernate-orm.schema-management.strategy=none (1)

quarkus.hibernate-orm.multitenant=SCHEMA (2)

quarkus.datasource.db-kind=postgresql (3)

quarkus.flyway.schemas=base,mycompany (4)
quarkus.flyway.locations=classpath:schema
quarkus.flyway.migrate-at-start=true

%prod.quarkus.datasource.username=quarkus_test
%prod.quarkus.datasource.password=quarkus_test
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://:5432/quarkus_test
1 禁用模式生成,因为它不受 Hibernate ORM 对模式多租户的支持。我们稍后将使用 Flyway。
2 启用模式多租户。

我们在这里使用默认数据源,但如果需要,可以通过此处的说明使用命名数据源。

3 配置数据源.
4 配置Flyway进行数据库初始化,因为 Hibernate ORM 的模式生成在此情况下不受支持。

这是 Flyway SQL(V1.0.0__create_fruits.sql)的示例,将在配置的文件夹 src/main/resources/schema 中创建。

CREATE SEQUENCE base.fruit_seq INCREMENT BY 50; -- 50 is quarkus default
CREATE TABLE base.fruit
(
    id   INT,
    name VARCHAR(40)
);
INSERT INTO base.fruit(id, name) VALUES (1, 'Cherry');
INSERT INTO base.fruit(id, name) VALUES (2, 'Apple');
INSERT INTO base.fruit(id, name) VALUES (3, 'Banana');
ALTER SEQUENCE base.fruit_seq RESTART WITH 4;

CREATE SEQUENCE mycompany.fruit_seq INCREMENT BY 50; -- 50 is quarkus default
CREATE TABLE mycompany.fruit
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO mycompany.fruit(id, name) VALUES (1, 'Avocado');
INSERT INTO mycompany.fruit(id, name) VALUES (2, 'Apricots');
INSERT INTO mycompany.fruit(id, name) VALUES (3, 'Blackberries');
ALTER SEQUENCE mycompany.fruit_seq RESTART WITH 4;

DATABASE 方法

对于每个租户,您需要创建一个命名数据源,其标识符与 TenantResolver 返回的标识符相同。

通过此方法,同一持久化单元使用的所有数据源都假定指向相同供应商(相同 db-kind)和版本的数据库。

不会检测到不匹配,并可能导致不可预测的行为。

数据源列表在构建时定义,因此通过此方法,租户的*列表*在*构建时固定*。如果租户列表需要在运行时更改,您必须以编程方式解析租户连接

quarkus.hibernate-orm.schema-management.strategy=none (1)

quarkus.hibernate-orm.multitenant=DATABASE (2)
quarkus.hibernate-orm.datasource=base (3)

# Default tenant 'base'
quarkus.datasource.base.db-kind=postgresql (4)
quarkus.flyway.base.locations=classpath:database/base (5)
quarkus.flyway.base.migrate-at-start=true
%prod.quarkus.datasource.base.username=base
%prod.quarkus.datasource.base.password=base
%prod.quarkus.datasource.base.jdbc.url=jdbc:postgresql://:5432/base

# Tenant 'mycompany'
quarkus.datasource.mycompany.db-kind=postgresql (6)
quarkus.flyway.mycompany.locations=classpath:database/mycompany (7)
quarkus.flyway.mycompany.migrate-at-start=true
%prod.quarkus.datasource.mycompany.username=mycompany
%prod.quarkus.datasource.mycompany.password=mycompany
%prod.quarkus.datasource.mycompany.jdbc.url=jdbc:postgresql://:5433/mycompany
1 禁用模式生成,因为它不受 Hibernate ORM 对数据库多租户的支持。我们稍后将使用 Flyway。
2 启用数据库多租户。
3 为持久化单元选择数据源。

这仅是为了允许 Quarkus 确定要使用的 Hibernate ORM 方言;有关详细信息,请参阅本节

4 为租户 base 配置数据源
5 为租户 base 配置Flyway进行数据库初始化,因为 Hibernate ORM 的模式生成在此情况下不受支持。
6 为另一个租户配置数据源

可能还有更多租户,但我们在这里仅限于两个。

7 为租户 mycompany 配置Flyway进行数据库初始化,因为 Hibernate ORM 的模式生成在此情况下不受支持。

以下是在配置的文件夹 src/main/resources/database 中创建的 Flyway SQL 文件的示例。

租户 base 的模式(src/main/resources/database/base/V1.0.0__create_fruits.sql

CREATE SEQUENCE fruit_seq INCREMENT BY 50; -- 50 is quarkus default
CREATE TABLE fruit
(
    id   INT,
    name VARCHAR(40)
);
INSERT INTO fruit(id, name) VALUES (1, 'Cherry');
INSERT INTO fruit(id, name) VALUES (2, 'Apple');
INSERT INTO fruit(id, name) VALUES (3, 'Banana');
ALTER SEQUENCE fruit_seq RESTART WITH 4;

租户 mycompany 的模式(src/main/resources/database/mycompany/V1.0.0__create_fruits.sql

CREATE SEQUENCE fruit_seq INCREMENT BY 50; -- 50 is quarkus default
CREATE TABLE fruit
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO fruit(id, name) VALUES (1, 'Avocado');
INSERT INTO fruit(id, name) VALUES (2, 'Apricots');
INSERT INTO fruit(id, name) VALUES (3, 'Blackberries');
ALTER SEQUENCE fruit_seq RESTART WITH 4;

DISCRIMINATOR 方法

默认数据源将用于所有租户。所有定义了用 @TenantId 注解的字段的实体都将自动填充该字段,并在查询中自动进行过滤。

quarkus.hibernate-orm.multitenant=DISCRIMINATOR (1)

quarkus.datasource.db-kind=postgresql (2)

%prod.quarkus.datasource.username=quarkus_test
%prod.quarkus.datasource.password=quarkus_test
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://:5432/quarkus_test
1 启用鉴别器多租户。
2 配置数据源.

以编程方式解析租户连接

如果您需要更动态的配置来支持不同的租户,并且不想在配置文件中出现多个条目,您可以使用 io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver 接口来实现您的连接检索逻辑。创建实现此接口的应用程序作用域 bean,并使用 @PersistenceUnitExtension(或对于命名持久化单元使用 @PersistenceUnitExtension("nameOfYourPU"))进行注解,这将替换 Quarkus 当前的默认实现 io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver。您的自定义连接解析器将允许例如在运行时根据数据库中的租户信息来创建每个租户的连接。

自动集成*不会*与以编程方式创建的 ConnectionProvider 一起使用。这意味着所有这些集成,包括来自其他 Quarkus 模块(例如SmallRye Health)的集成,都必须手动完成。

以下是使用标准 Quarkus 技术(Agroal 用于连接池,Narayana 用于事务)的 TenantConnectionResolver 实现的示例。

@ApplicationScoped
@PersistenceUnitExtension
public class ExampleTenantConnectionResolver implements TenantConnectionResolver {

    private final jakarta.transaction.TransactionManager transactionManager;
    private final TransactionSynchronizationRegistry transactionSynchronizationRegistry;

    public ExampleTenantConnectionResolver(
            TransactionManager transactionManager,
            TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
        this.transactionManager = transactionManager;
        this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
    }

    @Override
    public ConnectionProvider resolve(String tenantId) {
        // Use your own ConnectionProvider implementation here
        return new YourOwnCustomConnectionProviderImpl(createDatasource(tenantId));
    }

    private AgroalDataSource createDatasource(String tenantId) {
        try {
            final var txIntegration = new NarayanaTransactionIntegration(
                    transactionManager, transactionSynchronizationRegistry, null, false, null);
            // Fetch JDBC URL, username, password & other values from a per-tenant dynamic source
            final var dataSourceConfig = new AgroalDataSourceConfigurationSupplier()
                    .connectionPoolConfiguration(pc -> pc.initialSize(2)
                            .maxSize(10)
                            .minSize(2)
                            .maxLifetime(Duration.of(5, ChronoUnit.MINUTES))
                            .acquisitionTimeout(Duration.of(30, ChronoUnit.SECONDS))
                            .transactionIntegration(txIntegration)
                            .connectionFactoryConfiguration(
                                    cf -> cf.jdbcUrl("jdbc:postgresql://postgres:5432/" + tenantId)
                                            .credential(new NamePrincipal(username))
                                            .credential(new SimplePassword(password))));
            return AgroalDataSource.from(dataSourceConfig.get());
        } catch (SQLException ex) {
            throw new IllegalStateException(
                    "Failed to create a new data source based on the existing datasource configuration", ex);
        }
    }
}

拦截器

您可以通过简单地定义一个具有适当限定符的 CDI bean 来将 org.hibernate.Interceptor 分配给您的 SessionFactory

@PersistenceUnitExtension (1)
public static class MyInterceptor implements Interceptor, Serializable { (2)
    @Override
    public boolean onLoad(Object entity, Object id, Object[] state, (3)
            String[] propertyNames, Type[] types) {
        // ...
        return false;
    }
}
1 使用 @PersistenceUnitExtension 限定符注解拦截器实现,以告知 Quarkus 它应该在默认持久化单元中使用。

对于命名持久化单元,请使用 @PersistenceUnitExtension("nameOfYourPU")

2 根据需要实现 org.hibernate.Interceptor 的方法。

默认情况下,使用 @PersistenceUnitExtension 注解的拦截器 bean 是应用程序作用域的,这意味着每个应用程序只会创建一个拦截器实例,并在所有实体管理器之间重复使用。因此,拦截器实现必须是线程安全的。

为了每个实体管理器实例创建一个拦截器,请将您的 bean 注释为 @Dependent。在这种情况下,拦截器实现不需要是线程安全的。

由于 Hibernate ORM 本身的限制,@Dependent 作用域拦截器上的 @PreDestroy 方法永远不会被调用。

语句拦截器

您可以通过简单地定义一个具有适当限定符的 CDI bean 来将 org.hibernate.engine.jdbc.spi.StatementInspector 分配给您的 SessionFactory

@PersistenceUnitExtension (1)
public class MyStatementInspector implements StatementInspector { (2)
    @Override
    public String inspect(String sql) {
        // ...
        return sql;
    }
}
1 将语句拦截器实现注释为 @PersistenceUnitExtension 限定符,以告知 Quarkus 它应该在默认持久性单元中使用。

对于命名持久化单元,请使用 @PersistenceUnitExtension("nameOfYourPU")

2 实现 org.hibernate.engine.jdbc.spi.StatementInspector

自定义 JSON/XML 序列化/反序列化

默认情况下,Quarkus 将尝试根据可用的扩展自动配置格式映射器。当 Jackson (或 JSON-B) 可用时,全局配置的 ObjectMapper (或 Jsonb) 将用于序列化/反序列化操作。如果 Jackson 和 JSON-B 同时可用,Jackson 将优先。

Hibernate ORM 中的 JSON 和 XML 序列化/反序列化可以通过实现 org.hibernate.type.format.FormatMapper 并使用适当的限定符注释实现来定制。

import io.quarkus.hibernate.orm.JsonFormat;
import org.hibernate.type.format.FormatMapper;

@JsonFormat (1)
@PersistenceUnitExtension (2)
public class MyJsonFormatMapper implements FormatMapper { (3)
    @Override
    public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
        // ...
    }

    @Override
    public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
       // ...
    }
}
1 将格式映射器实现注释为 @JsonFormat 限定符,以告知 Quarkus 该映射器特定于 JSON 序列化/反序列化。
确保使用 Quarkus 特定的 @io.quarkus.hibernate.orm.JsonFormat 注释,而不是 Jackson 的。
2 将格式映射器实现注释为 @PersistenceUnitExtension 限定符,以告知 Quarkus 它应该在默认持久性单元中使用。

对于命名持久化单元,请使用 @PersistenceUnitExtension("nameOfYourPU")

3 实现 org.hibernate.type.format.FormatMapper

对于自定义 XML 格式映射器,必须应用不同的 CDI 限定符。

import io.quarkus.hibernate.orm.XmlFormat;
import org.hibernate.type.format.FormatMapper;

@XmlFormat (1)
@PersistenceUnitExtension (2)
public class MyJsonFormatMapper implements FormatMapper { (3)
    @Override
    public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
        // ...
    }

    @Override
    public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
       // ...
    }
}
1 将格式映射器实现注释为 @XmlFormat 限定符,以告知 Quarkus 该映射器特定于 XML 序列化/反序列化。
2 将格式映射器实现注释为 @PersistenceUnitExtension 限定符,以告知 Quarkus 它应该在默认持久性单元中使用。

对于命名持久化单元,请使用 @PersistenceUnitExtension("nameOfYourPU")

3 实现 org.hibernate.type.format.FormatMapper

格式映射器必须同时具有 @PersistenceUnitExtension@JsonFormat@XmlFormat CDI 限定符。

为同一持久性单元注册多个 JSON (或 XML) 格式映射器将由于歧义而导致异常。

验证模式和 Hibernate Validator 集成

Hibernate Validator 与 Hibernate ORM 的集成提供了以下功能:

  • 在生命周期事件上执行实体验证

  • 将约束信息从实体应用于 DDL

从 Quarkus 的角度来看,这由 quarkus.hibernate-orm.validation.mode 配置属性 控制。可用的验证模式是:

  • auto — 默认选项;当应用程序使用 quarkus-hibernate-validator 时,其行为与同时启用 callbackddl 相同,否则与 none 相同。

  • callback — Hibernate Validator 将执行生命周期事件验证。

  • ddl — Hibernate Validator 约束将考虑用于 DDL 操作

  • none — 将禁用 Hibernate Validator 集成。

虽然所有具有相应 Jakarta Validation 规则的约束将在 callback 验证期间应用,但在 ddl 模式下,只有一部分约束用于影响 DDL。在 Hibernate Validator 文档中,您可以找到 可用约束列表 及其对 DDL 生成的影响。

让我们考虑一个带有应用于其属性的约束的简单实体。

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@Entity
public class MyEntity {
    @Id
    @GeneratedValue
    public long id;

    @NotNull
    @NotEmpty
    @Size(max = 50)
    public String name;

    public String value;
}

当启用 ddl 模式时,预期的结果模式将具有以下约束:

create table myentity
(
    id    bigint      not null  primary key,
    name  varchar(50) not null, (1)
    value varchar(255),         (2)
);
1 name 列由于 @NotNull 约束而具有 not null 约束,并且由于 @Size(max=50) 约束,值的长度限制为 50
2 由于实体中的 value 属性没有任何约束,DDL 中没有其他约束,并且 255 的长度限制来自默认的 jakarta.persistence.Column#lenght()

使用 callback 模式,预期会抛出 jakarta.validation.ConstraintViolationException

try {
    MyEntity entity = new MyEntity();
    entity.setName(veryLongName);
    em.persist(entity);
    em.flush();
} catch (ConstraintViolationException exception) {
    // handle the constraint violations somehow
}

由于 Quarkus 具有 jakarta.validation.ConstraintViolationException 的内置异常映射器,显式处理这些异常可能没有必要。有关更多详细信息,请参阅 Hibernate Validator 指南的 REST 端点验证 部分。

静态元模型和 Jakarta Data

Hibernate ORM 的静态元模型和 Jakarta Data 功能都通过 hibernate-processor 注释处理器在 Quarkus 中可用。由于它是一个注释处理器,您必须在构建工具中对其进行相应配置。

pom.xml
<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.hibernate.orm</groupId>
                <artifactId>hibernate-processor</artifactId>
                <!-- Note, no artifact version is required, it's managed by Quarkus.  -->
            </path>
            <!-- other processors that may be required by your app -->
        </annotationProcessorPaths>
        <!-- Other compiler plugin configuration options -->
    </configuration>
</plugin>
build.gradle
// Enforce the version management of your annotation processor dependencies,
// so that there's no need to define an explicit version of the hibernate-processor
annotationProcessor enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
annotationProcessor 'org.hibernate.orm:hibernate-processor'

静态元模型

生成的静态元模型允许以类型安全的方式构建查询。让我们考虑一个简单的实体。

@Entity
public class MyEntity {
    @Id
    @GeneratedValue
    public Integer id;

    @Column(unique = true)
    public String name;
}

使用静态元模型创建的查询可能如下所示:

var builder = session.getCriteriaBuilder();
var criteria = builder.createQuery(MyEntity.class);
var e = criteria.from(MyEntity_.class);
criteria.where(e.get(MyEntity_.name).equalTo(name));
var query = session.createQuery(criteria);
var result = query.list();

有关静态元模型的更详细概述,请参阅 Jakarta Persistence 规范

Jakarta Data

Jakarta Data 除了需要 hibernate-processor 注释处理器到位之外,还需要添加一个额外的依赖项。

pom.xml
<dependency>
    <groupId>jakarta.data</groupId>
    <artifactId>jakarta.data-api</artifactId>
</dependency>
build.gradle
implementation 'jakarta.data:jakarta.data-api'

有了这个依赖项和注释处理器,您可以像这样简单地创建您的存储库:

@Repository
public interface MyRepository extends CrudRepository<MyEntity, Integer> { (1)

    @Query("select e from MyEntity e where e.name like :name") (2)
    List<MyEntity> findByName(String name);

    @Delete (3)
    void delete(String name);

}
  1. 为了跳过 CRUD 操作的样板定义,我们可以使用可用的接口之一 (例如 CrudRepositoryBasicRepository)。

  2. 通过向 @Query 注释提供查询字符串,添加带有参数的自定义查询非常简单。

  3. 如果 Jakarta Data 接口的基本 CRUD 操作不够,我们可以始终添加一个自定义操作,在这种情况下,是一个删除操作,它按名称删除 `MyEntity`。

然后,存储库可以像任何其他 bean 一样使用:

public class MyEntityResource {

    @Inject
    MyRepository repository;

    @POST
    @Transactional
    public void create(MyEntity entity) {
        repository.insert(entity);
    }

	// ...

}

请参阅相应的 Hibernate Data RepositoriesJakarta Data 指南,了解它们还提供哪些功能。

相关内容