Using Hibernate ORM and Jakarta Persistence

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

Hibernate ORM is the de facto standard Jakarta Persistence (formerly known as JPA) implementation and offers you the full breadth of an Object Relational Mapper. It works beautifully in Quarkus.

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

该解决方案位于 hibernate-orm-quickstart directory 中。

The solution is located in the hibernate-orm-quickstart directory.

Setting up and configuring Hibernate ORM

在 Quarkus 中使用 Hibernate ORM 时,您无需拥有 persistence.xml 资源来对其进行配置。

When using Hibernate ORM in Quarkus, you don’t need to have a persistence.xml resource to configure it.

使用这种经典的配置文件是一种选择,但是如果没有具体的高级需求,这是不需要的;因此,我们将首先了解如何在没有 persistence.xml 资源的情况下配置 Hibernate ORM。

Using such a classic configuration file is an option, but unnecessary unless you have specific advanced needs; so we’ll see first how Hibernate ORM can be configured without a persistence.xml resource.

在 Quarkus 中,您只需:

In Quarkus, you only need to:

  • add your configuration settings in application.properties

  • annotate your entities with @Entity and any other mapping annotation as usual

自动执行其他配置需求:Quarkus 将做出一些主观的选项和有根据的猜测。

Other configuration needs have been automated: Quarkus will make some opinionated choices and educated guesses.

添加以下依赖关系到您的项目:

Add the following dependencies to your project:

  • the Hibernate ORM extension: io.quarkus:quarkus-hibernate-orm

  • your JDBC driver extension; the following options are available:

例如:

For instance:

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 中添加相关的配置属性。

Annotate your persistent objects with @Entity, then add the relevant configuration properties in application.properties.

Example application.properties
quarkus.datasource.db-kind = postgresql 1
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db

quarkus.hibernate-orm.database.generation=drop-and-create 2
1 Configure the datasource.
2 Drop and create the database at startup (use update to only update the schema).

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

Note that these configuration properties are not the same ones as in your typical Hibernate ORM configuration file. They will often map to Hibernate ORM configuration properties but could have different names and don’t necessarily map 1:1 to each other.

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

Also, Quarkus will set many Hibernate ORM configuration settings automatically, and will often use more modern defaults.

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

For a list of the items that you can set in application.properties, see hibernate-configuration-properties.

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

An EntityManagerFactory will be created based on the Quarkus datasource configuration as long as the Hibernate ORM extension is listed among your project dependencies.

方言将根据您的数据源自动选择和配置;您可能需要 configure it to more precisely match your database

The dialect will be selected and configured automatically based on your datasource; you may want to hibernate-dialect.

您便可以快乐地注入您的 EntityManager

You can then happily inject your EntityManager:

Example application bean using Hibernate
@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 Inject your entity manager and have fun
2 Mark your CDI bean method as @Transactional and the EntityManager will enlist and flush at commit.
Example Entity
@Entity
public class Gift {
    private Long id;
    private String name;

    @Id
    @SequenceGenerator(name = "giftSeq", sequenceName = "gift_id_seq", allocationSize = 1, initialValue = 1)
    @GeneratedValue(generator = "giftSeq")
    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;
    }
}

如果要在 Hibernate ORM 启动时加载 SQL 语句,请向 `resources`目录的根目录中添加一个 `import.sql`文件。此脚本可以包含任何 SQL DML 语句。务必让每条语句以分号结束。

To load SQL statements when Hibernate ORM starts, add an import.sql file to the root of your resources directory. This script can contain any SQL DML statements. Make sure to terminate each statement with a semicolon.

这可用于为测试或演示准备好数据集。

This is useful to have a data set ready for your tests or demos.

务必在事务中封装修改数据库的方法(例如 entity.persist())。将 CDIB bean 方法标记为 `@Transactional`将为您执行该操作并将该方法设为事务边界。我们建议在您的应用程序入口点边界(例如您的 REST 端点控制器)中执行此操作。

Make sure to wrap methods modifying your database (e.g. entity.persist()) within a transaction. Marking a CDI bean method @Transactional will do that for you and make that method a transaction boundary. We recommend doing so at your application entry point boundaries like your REST endpoint controllers.

Dialect

Supported databases

对于 supported databases,不必明确设置 Hibernate ORM dialect:它会根据数据源自动选择。

For supported databases, the Hibernate ORM dialect does not need to be set explicitly: it is selected automatically based on the datasource.

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

By default, the dialect is configured to target the minimum supported version of the database.

为了让 Hibernate ORM 生成更高效的 SQL,以避免解决方法并充分利用更多数据库功能,您可以明确设置数据库版本:

In order for Hibernate ORM to generate more efficient SQL, to avoid workarounds and to take advantage of more database features, you can set the database version explicitly:

application.properties with an explicit db-version
quarkus.datasource.db-kind = postgresql
quarkus.datasource.db-version = 14.0 1
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db
1 Set the database version. The Hibernate ORM dialect will target that version.

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

As a rule, the version set here should be as high as possible, but must be lower than or equal to the version of any database your application will connect to.

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

As described above, the version can either be preconfigured explicitly via a quarkus.datasource.db-version configuration property, or implicitly set by the Quarkus build process to a minimum supported version of the database. Quarkus will try to check this preconfigured version against the actual database version on startup, leading to a startup failure when the actual version is lower.

这是因为 Hibernate ORM 可能会生成对于比配置版本旧的数据库不可用的 SQL,这将导致运行时异常。

This is because Hibernate ORM may generate SQL that is invalid for versions of the database older than what is configured, which would lead to runtime exceptions.

如果无法访问数据库,将记录警告,但启动将继续。

If the database cannot be reached, a warning will be logged but startup will proceed.

Other databases

如果 your database does not have a corresponding Quarkus extension,或如果出于某种原因,默认值不满足您的需求,您将需要明确设置 Hibernate ORM dialect

If your database does not have a corresponding Quarkus extension, or if the defaults do not match your needs for some reason, you will need to set the Hibernate ORM dialect explicitly:

application.properties with an explicit dialect
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:26257/hibernate_db

quarkus.hibernate-orm.dialect=Cockroach 1
1 Set the Hibernate ORM dialect.[.iokays-translated-8951b516d20be569399a54ec21330fd5] 对于内置方言,预期值是 official list of dialects中的一个名称,*without*以 Dialect`后缀表示,例如 `Cockroach`以表示 `CockroachDialect

For built-in dialects, the expected value is one of the names in the official list of dialects, without the Dialect suffix, for example Cockroach for CockroachDialect.

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

For third-party dialects, the expected value is the fully-qualified class name, for example com.acme.hibernate.AcmeDbDialect.

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

In that case, keep in mind that the JDBC driver or Hibernate ORM dialect may not work properly in GraalVM native executables.

supported databases 一样,您可以显式配置数据库版本,以充分利用 Hibernate ORM:

As with hibernate-dialect-supported-databases, you can configure the DB version explicitly to get the most out of Hibernate ORM:

application.properties with an explicit dialect and db-version
quarkus.datasource.db-kind = postgresql
quarkus.datasource.db-version = 22.2 1
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:26257/hibernate_db

quarkus.hibernate-orm.dialect=Cockroach 2
1 Set the database version. The Hibernate ORM dialect will target that version. Since we’re targeting CockroachDB here, we’re passing the CockroachDB version, not the PostgreSQL version.
2 Set the Hibernate ORM dialect.

Varying database

在启用 database multi-tenancy 时,Hibernate ORM 将在运行时针对同一持久化单元使用多个数据源,并且 Quarkus 默认情况下无法分辨将使用哪个数据源,因此它将无法检测到在 Hibernate ORM 中要使用的方言。

When enabling database-approach, Hibernate ORM will use multiple datasources at runtime for the same persistence unit, and by default Quarkus cannot tell which datasource is going to be used, so it will not be able to detect a dialect to use in Hibernate ORM.

因此,在启用 database multi-tenancy 时,建议显式将 Hibernate ORM 配置指向在运行时要使用的那些数据源中的一个数据源,例如使用 quarkus.hibernate-orm.datasource=base(base 是数据源的名称)。

For that reason, when enabling database-approach, it is recommended to explicitly point the Hibernate ORM configuration to one datasource among those that will be used at runtime, e.g. with quarkus.hibernate-orm.datasource=base (base being the name of a datasource).

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

When doing so, Quarkus will infer the database version and (if possible) dialect from that datasource. For unsupported databases, you may still need to set the Hibernate ORM dialect explicitly, as explained in hibernate-dialect-other-databases.

Hibernate ORM configuration properties

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

There are various optional properties useful to refine your EntityManagerFactory or guide guesses of Quarkus.

只要配置了默认数据源,就不需要任何必需的属性。

There are no required properties, as long as a default datasource is configured.

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

When no property is set, Quarkus can typically infer everything it needs to set up Hibernate ORM and will have it use the default datasource.

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

The configuration properties listed here allow you to override such defaults, and customize and tune various aspects.

Unresolved directive in hibernate-orm.adoc - include::{generated-dir}/config/quarkus-hibernate-orm.adoc[]

请勿在 application.properties 中混合 <<`persistence.xml`,persistence-xml>> 和 quarkus.hibernate-orm.* 属性。Quarkus 将引发异常。对要使用的方法下定决心。

Do not mix <<`persistence.xml`,persistence-xml>> and quarkus.hibernate-orm.* properties in application.properties. Quarkus will raise an exception. Make up your mind on which approach you want to use.

如果您的类路径包含要忽略的 persistence.xml,请设置以下配置属性:

If your classpath contains a persistence.xml that you want to ignore, set the following configuration property:

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

想在 Docker 中启动 PostgreSQL 服务器吗?

Want to start a PostgreSQL server on the side with Docker?

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

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

This will start a non-durable empty database: ideal for a quick experiment!

Multiple persistence units

Setting up multiple persistence units

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

It is possible to define multiple persistence units using the Quarkus configuration properties.

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

The properties at the root of the quarkus.hibernate-orm. namespace define the default persistence unit. For instance, the following snippet defines a default datasource and a default persistence unit:

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

quarkus.hibernate-orm.database.generation=drop-and-create

使用基于映射的方法,可以定义命名的持久性单元:

Using a map based approach, it is possible to define named persistence units:

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".database.generation=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".database.generation=drop-and-create 6
quarkus.hibernate-orm."inventory".datasource=inventory
quarkus.hibernate-orm."inventory".packages=org.acme.model.inventory
1 Define a datasource named users.
2 Define a datasource named inventory.
3 Define a persistence unit called users.
4 Define the datasource used by the persistence unit.
5 This configuration property is important, but we will discuss it a bit later.
6 Define a persistence unit called inventory pointing to the inventory datasource.

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

You can mix the default datasource and named datasources or only have one or the other.

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

The default persistence unit points to the default datasource by default. For named persistence units, the datasource property is mandatory. You can point your persistence unit to the default datasource by setting it to <default> (which is the internal name of the default datasource).

完全可以使多个持久性单元指向同一个数据源。

It is perfectly valid to have several persistence units pointing to the same datasource.

Attaching model classes to persistence units

有两种方式可以将模型类附加到持久性单元,并且不应混用这两种方式:

There are two ways to attach model classes to persistence units, and they should not be mixed:

  • Via the packages configuration property;

  • Via the @io.quarkus.hibernate.orm.PersistenceUnit package-level annotation.

如果两者混合,则将忽略注释,仅考虑 packages 配置属性。

If both are mixed, the annotations are ignored and only the packages configuration properties are taken into account.

使用 packages 配置属性很简单:

Using the packages configuration property is simple:

quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.packages=org.acme.model.defaultpu

quarkus.hibernate-orm."users".database.generation=drop-and-create
quarkus.hibernate-orm."users".datasource=users
quarkus.hibernate-orm."users".packages=org.acme.model.user

此配置代码段将创建两个持久性单元:

This configuration snippet will create two persistence units:

  • The default one which will contain all the model classes under the org.acme.model.defaultpu package, subpackages included.

  • A named users persistence unit which will contain all the model classes under the org.acme.model.user package, subpackages included.

你可以将多个包附加到持久性单元:

You can attach several packages to a persistence unit:

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

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

All the model classes under the org.acme.model.shared and org.acme.model.user packages will be attached to the users persistence unit.

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

It is also supported to attach a given model class to several persistence units.

模型类需要按照既定顺序添加到给定的持久性单元。也就是说,给定实体的所有依赖模型类(映射超类、嵌入式…​)都必须附加到持久性单元。由于我们在包级别处理持久性单元,所以它应该是简单的。

Model classes need to be consistently added to a given persistence unit. That meant that all dependent model classes of a given entity (mapped super classes, embeddables…​) are required to be attached to the persistence unit. As we are dealing with the persistence unit at the package level, it should be simple enough.

Panache 实体可以附加到仅一个持久性单元。

Panache entities can be attached to only one persistence unit.

对于附加到多个持久性单元的实体,你无法使用 Panache。但你可以将这两种方法混合使用,并混合使用需要多个持久性单元的 Panache 实体和传统实体。

For entities attached to several persistence units, you cannot use Panache. You can mix the two approaches though and mix Panache entities and traditional entities where multiple persistence units are required.

如果你有此用例并且有关于如何在不混淆简化 Panache 方法的情况下实现它的巧妙想法,请联系 quarkus-dev mailing list 上的我们。

If you have a use case for that and clever ideas about how to implement it without cluttering the simplified Panache approach, contact us on the quarkus-dev mailing list.

连接持久性单元和模型类的第二种方法是使用包级别的 @io.quarkus.hibernate.orm.PersistenceUnit 注释。再次强调,这两种方法不能混用。

The second approach to attach model classes to a persistence unit is to use package-level @io.quarkus.hibernate.orm.PersistenceUnit annotations. Again, the two approaches cannot be mixed.

若要使用 packages 配置属性获取类似上述内容的配置,请使用以下内容创建 package-info.java 文件:

To obtain a configuration similar to the one above with the packages configuration property, create a package-info.java file with the following content:

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

import io.quarkus.hibernate.orm.PersistenceUnit;
1 Be careful, use the @io.quarkus.hibernate.orm.PersistenceUnit annotation, not the Jakarta Persistence one.

我们仅支持使用 @PersistenceUnit 注释在包级别为模型类定义 @PersistenceUnit,而暂不支持在类级别使用。

We only support defining the @PersistenceUnit for model classes at the package level, using the @PersistenceUnit annotation at the class level is not supported in this case.

请注意,与使用配置属性类似,我们考虑了注释的包及其所有子包。

Note that, similarly to what we do with the configuration property, we take into account the annotated package but also all its subpackages.

CDI integration

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

If you are familiar with using Hibernate ORM in Quarkus, you probably already have injected the EntityManager using CDI:

@Inject
EntityManager entityManager;

此操作将注入默认持久性单元的 EntityManager

This will inject the EntityManager of the default persistence unit.

注入已命名持久性单元的 EntityManager(在我们的示例中为 users)非常简单:

Injecting the EntityManager of a named persistence unit (users in our example) is as simple as:

@Inject
@PersistenceUnit("users") 1
EntityManager entityManager;
1 Here again, we use the same @io.quarkus.hibernate.orm.PersistenceUnit annotation.

您可以使用完全相同的机制注入已命名持久性单元的 EntityManagerFactory

You can inject the EntityManagerFactory of a named persistence unit using the exact same mechanism:

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

Activate/deactivate persistence units

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

If a persistence unit is configured at build time, by default it is active at runtime, that is Quarkus will start the corresponding Hibernate ORM SessionFactory on application startup.

若要在运行时停用持久性单元,请将 quarkus.hibernate-orm[.optional name].active 设置为 false。然后,Quarkus 将不会在应用程序启动时启动相应的 Hibernate ORM SessionFactory。尝试在运行时使用相应的持久性单元的操作会失败,并显示明确的错误消息。

To deactivate a persistence unit at runtime, set quarkus.hibernate-orm[.optional name].active to false. Then Quarkus will not start the corresponding Hibernate ORM SessionFactory on application startup. Any attempt to use the corresponding persistence unit at runtime will fail with a clear error message.

这在您希望应用程序能够运行时重新连接时特别有用。

This is in particular useful when you want an application to be able to use one of a pre-determined set of datasources at runtime.

例如,使用以下配置:

For example, with the following configuration:

quarkus.hibernate-orm."pg".packages=org.acme.model.shared
quarkus.hibernate-orm."pg".datasource=pg
quarkus.hibernate-orm."pg".database.generation=drop-and-create
quarkus.hibernate-orm."pg".active=false
quarkus.datasource."pg".db-kind=h2
quarkus.datasource."pg".active=false
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".database.generation=drop-and-create
quarkus.hibernate-orm."oracle".active=false
quarkus.datasource."oracle".db-kind=oracle
quarkus.datasource."oracle".active=false
quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database

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

Setting quarkus.hibernate-orm."pg".active=true and quarkus.datasource."pg".active=true at runtime will make only the PostgreSQL persistence unit and datasource available, and setting quarkus.hibernate-orm."oracle".active=true and quarkus.datasource."oracle".active=true at runtime will make only the Oracle persistence unit and datasource available.

Custom configuration profiles可以帮助简化这样的设置。通过将以下特定于配置文件的配置附加到上面的配置,您可以简单地通过 setting quarkus.profile: `quarkus.profile=prod,pg`或 `quarkus.profile=prod,oracle`选择一个持久性单元/datasource。

Custom configuration profiles can help simplify such a setup. By appending the following profile-specific configuration to the one above, you can select a persistence unit/datasource at runtime simply by setting quarkus.profile: quarkus.profile=prod,pg or quarkus.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."

定义重定向至当前活动持久性单元的 CDI bean producer 也很有帮助,如下所示:

It can also be useful to define a CDI bean producer redirecting to the currently active persistence unit, like this:

public class MyProducer {
    @Inject
    DataSourceSupport dataSourceSupport;

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

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

    @Produces
    @ApplicationScoped
    public Session session() {
        if (dataSourceSupport.getInactiveNames().contains("pg")) {
            return oracleSessionBean;
        } else {
            return pgSessionBean;
        }
    }
}

Setting up and configuring Hibernate ORM with a persistence.xml

或者,可以使用 META-INF/persistence.xml 来设置 Hibernate ORM。此方法适用于:

Alternatively, you can use a META-INF/persistence.xml to set up Hibernate ORM. This is useful for:

  • migrating existing code

  • when you have relatively complex settings requiring the full flexibility of the configuration

  • or if you like it the good old way

如果你使用 persistence.xml,那么就无法使用 quarkus.hibernate-orm.* 属性,并且只考虑在 persistence.xml 中定义的可持久化单元。

If you use a persistence.xml, then you cannot use the quarkus.hibernate-orm.* properties and only persistence units defined in persistence.xml will be taken into account.

如果您的类路径包含要忽略的 persistence.xml,请设置以下配置属性:

If your classpath contains a persistence.xml that you want to ignore, set the following configuration property:

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

你的 pom.xml 依赖项和 Java 代码将与此前示例相同。唯一不同的是,你可以在 META-INF/persistence.xml 中指定 Hibernate ORM 配置:

Your pom.xml dependencies as well as your Java code would be identical to the precedent example. The only difference is that you would specify your Hibernate ORM configuration in META-INF/persistence.xml:

Example persistence.xml resource
<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,因此在这种情况下的适当参考是 documentation on hibernate.org

When using the persistence.xml configuration you are configuring Hibernate ORM directly, so in this case the appropriate reference is the documentation on hibernate.org.

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

Please remember these are not the same property names as the ones used in the Quarkus application.properties, nor will the same defaults be applied.

XML mapping

Quarkus 中的 Hibernate ORM 支持 XML 映射。你可以按照 orm.xml format (Jakarta Persistence)hbm.xml format (specific to Hibernate ORM, deprecated) 添加映射文件:

Hibernate ORM in Quarkus supports XML mapping. You can add mapping files following the orm.xml format (Jakarta Persistence) or the hbm.xml format (specific to Hibernate ORM, deprecated):

  • in application.properties through the (build-time) quarkus.hibernate-orm.mapping-files property.

  • in <<`persistence.xml`,persistence-xml>> through the <mapping-file> element.

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

XML mapping files are parsed at build time.

如果 META-INF/orm.xml 文件存在于类路径中,那么它将始终按默认设置包括在内。

The file META-INF/orm.xml will always be included by default, if it exists in the classpath.

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

If that is not what you want, use quarkus.hibernate-orm.mapping-files = no-file or <mapping-file>no-file</mapping-file>.

Defining entities in external projects or jars

Quarkus 中的 Hibernate ORM 依赖于实体的编译期字节码增强。如果你在构建你的 Quarkus 应用程序的同一项目中定义你的实体,那么一切都会正常运行。

Hibernate ORM in Quarkus relies on compile-time bytecode enhancements to your entities. If you define your entities in the same project where you build your Quarkus application, everything will work fine.

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

If the entities come from external projects or jars, you can make sure that your jar is treated like a Quarkus application library by adding an empty META-INF/beans.xml file.

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

This will allow Quarkus to index and enhance your entities as if they were inside the current project.

Hibernate ORM in development mode

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

Quarkus development mode is really useful for applications that mix front end or services and database access.

有一些常见方法可以充分利用它。

There are a few common approaches to make the best of it.

第一种选择是将 quarkus.hibernate-orm.database.generation=drop-and-createimport.sql 结合使用。

The first choice is to use quarkus.hibernate-orm.database.generation=drop-and-create in conjunction with import.sql.

这样,对于你的应用程序的每一次更改,尤其是对你的实体的更改,数据库模式将被正确重建,并且你的数据固定装置(存储在 import.sql 中)将被用于从头开始重新填充它。这最适合完全控制你的环境,并且适用于 Quarkus 热重载模式:你的实体更改或对 import.sql 的任何更改都会立即被采纳,并且模式会在不重启应用程序的情况下更新!

That way for every change to your app and in particular to your entities, the database schema will be properly recreated and your data fixture (stored in import.sql) will be used to repopulate it from scratch. This is best to perfectly control your environment and works magic with Quarkus live reload mode: your entity changes or any change to your import.sql is immediately picked up and the schema updated without restarting the application!

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

By default, in dev and test modes, Hibernate ORM, upon boot, will read and execute the SQL statements in the /import.sql file (if present). You can change the file name by changing the property quarkus.hibernate-orm.sql-load-script in application.properties.

第二种方法是使用 quarkus.hibernate-orm.database.generation=update。当你要进行许多实体更改,但仍然需要处理生产数据的副本,或者当你想重现一个基于特定数据库条目的错误时,这种方法是最好的。update 是 Hibernate ORM 尽力想要达成的,但它会在特定情况下失败,包括会带来数据丢失的更改数据库结构。例如,如果你更改了违反外键约束的结构,Hibernate ORM 可能会被迫退出。但在开发中,这些限制是可以接受的。

The second approach is to use quarkus.hibernate-orm.database.generation=update. This approach is best when you do many entity changes but still need to work on a copy of the production data or if you want to reproduce a bug that is based on specific database entries. update is a best effort from Hibernate ORM and will fail in specific situations including altering your database structure which could lead to data loss. For example if you change structures which violate a foreign key constraint, Hibernate ORM might have to bail out. But for development, these limitations are acceptable.

第三种方法是使用 quarkus.hibernate-orm.database.generation=none。当你要处理生产数据的一个副本,但是希望完全控制架构演进时,这种方法是最好的。或者,如果你使用的数据库架构迁移工具类似于 FlywayLiquibase

The third approach is to use quarkus.hibernate-orm.database.generation=none. This approach is best when you are working on a copy of the production data but want to fully control the schema evolution. Or if you use a database schema migration tool like Flyway or Liquibase.

使用此方法在对实体进行更改时,确保相应地调整数据库架构;你也可以使用 validate 同时调整 Hibernate 以验证架构是否符合它的预期。

With this approach when making changes to an entity, make sure to adapt the database schema accordingly; you could also use validate to have Hibernate verify the schema matches its expectations.

不要在你的生产环境中使用 quarkus.hibernate-orm.database.generation drop-and-createupdate

Do not use quarkus.hibernate-orm.database.generation drop-and-create and update in your production environment.

当与 Quarkus 配置文件合并时,这些方法会变得非常强大。你可以定义不同的 configuration profiles 以根据你的环境选择不同的行为。这对你是极好的,因为你可以定义不同的 Hibernate ORM 属性组合,从而符合你当前需要的开发风格。

These approaches become really powerful when combined with Quarkus configuration profiles. You can define different configuration profiles to select different behaviors depending on your environment. This is great because you can define different combinations of Hibernate ORM properties matching the development style you currently need.

application.properties
%dev.quarkus.hibernate-orm.database.generation = drop-and-create
%dev.quarkus.hibernate-orm.sql-load-script = import-dev.sql

%dev-with-data.quarkus.hibernate-orm.database.generation = update
%dev-with-data.quarkus.hibernate-orm.sql-load-script = no-file

%prod.quarkus.hibernate-orm.database.generation = none
%prod.quarkus.hibernate-orm.sql-load-script = no-file

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

You can start dev mode using a custom profile:

include::{includes}/devtools/dev.adoc[]:!dev-additional-parameters:

Unresolved directive in hibernate-orm.adoc - include::{includes}/devtools/dev.adoc[] :!dev-additional-parameters:

Hibernate ORM in production mode

Quarkus 带有默认配置文件(devtest`和 `prod)。且你可以添加你自己的自定义配置文件来描述多种环境(staging、`prod-us`等)。

Quarkus comes with default profiles (dev, test and prod). And you can add your own custom profiles to describe various environments (staging, prod-us, etc).

Hibernate ORM Quarkus 扩展程序在开发和测试模式中将一些默认配置设置得不同于其他环境。

The Hibernate ORM Quarkus extension sets some default configurations differently in dev and test modes than in other environments.

  • quarkus.hibernate-orm.sql-load-script is set to no-file for all profiles except the dev and test ones.

你可以在你的 application.properties 中明确地覆盖它(例如 %prod.quarkus.hibernate-orm.sql-load-script = import.sql),但我们希望你避免在生产中意外覆盖你的数据库 :)

You can override it in your application.properties explicitly (e.g. %prod.quarkus.hibernate-orm.sql-load-script = import.sql) but we wanted you to avoid overriding your database by accident in prod :)

说起来,确保在生产中不要放弃你的数据库架构!在你的属性文件中添加以下内容。

Speaking of, make sure to not drop your database schema in production! Add the following in your properties file.

application.properties
%prod.quarkus.hibernate-orm.database.generation = none
%prod.quarkus.hibernate-orm.sql-load-script = no-file

Automatically transitioning to Flyway to Manage Schemas

如果你在运行开发模式时安装了 Flyway extension,Quarkus 提供了一种简单的方法来初始化你的 Flyway 配置,其中使用 Hibernate ORM 自动生成的架构。这旨在减轻从早期开发阶段(其中 Hibernate 可以用来快速建立架构)到生产阶段(其中 Flyway 用于管理架构更改)的过渡。

If you have the Flyway extension installed when running in development mode, Quarkus provides a simple way to initialize your Flyway configuration using the schema generated automatically by Hibernate ORM. This is intended to ease the move from the early development phase, where Hibernate can be used to quickly set up the schema, to the production phase, where Flyway is used to manage schema changes.

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

To use this feature simply open the Dev UI when the quarkus-flyway extension is installed and click in the Datasources link in the Flyway pane. Hit the Create Initial Migration button and the following will happen:

  • A db/migration/V1.0.0__{appname}.sql file will be created, containing the SQL Hibernate is running to generate the schema

  • quarkus.flyway.baseline-on-migrate will be set, telling Flyway to automatically create its baseline tables

  • quarkus.flyway.migrate-at-start will be set, telling Flyway to automatically apply migrations on application startup

  • %dev.quarkus.flyway.clean-at-start and %test.quarkus.flyway.clean-at-start will be set, to clean the DB after reload in dev/test mode

此按钮只是一种便利的功能,可以帮助你快速上手 Flyway,由你来确定如何在生产中管理你的数据库架构。特别是,migrate-at-start 设置可能并不适用于所有环境。

This button is simply a convenience to quickly get you started with Flyway, it is up to you to determine how you want to manage your database schemas in production. In particular the migrate-at-start setting may not be right for all environments.

Caching

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

Applications that frequently read the same entities can see their performance improved when the Hibernate ORM second-level cache is enabled.

Caching of entities

要启用二级缓存,请使用 `@jakarta.persistence.Cacheable`标记你希望缓存的实体:

To enable second-level cache, mark the entities that you want cached with @jakarta.persistence.Cacheable:

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

当某个实体使用 `@Cacheable`进行了注释时,除集合和其他实体的关系之外的所有字段值均会被缓存。

When an entity is annotated with @Cacheable, all its field values are cached except for collections and relations to other entities.

这意味着实体可以在不查询数据库的情况下加载,但需要注意的是这说明加载的实体可能无法反映数据库中的近期更改。

This means the entity can be loaded without querying the database, but be careful as it implies the loaded entity might not reflect recent changes in the database.

Caching of collections and relations

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

Collections and relations need to be individually annotated to be cached; in this case the Hibernate specific @org.hibernate.annotations.Cache should be used, which requires also to specify the CacheConcurrencyStrategy:

package org.acme;

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

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

    // ...
}

Caching of queries

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

Queries can also benefit from second-level caching. Cached query results can be returned immediately to the caller, avoiding to run the query on the database.

需要注意的是这说明结果可能无法反映近期更改。

Be careful as this implies the results might not reflect recent changes.

若要缓存查询,请在 `Query`实例上将此查询标记为可缓存的:

To cache a query, mark it as cacheable on the Query instance:

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

如果你有一个 NamedQuery,则可以在其定义(通常会在实体上)直接启用缓存:

If you have a NamedQuery then you can enable caching directly on its definition, which will usually be on an entity:

@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 中已经默认集成并启用,因此只需要设置哪些内容可以安全缓存。

That’s all! Caching technology is already integrated and enabled by default in Quarkus, so it’s enough to set which ones are safe to be cached.

Tuning of Cache Regions

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

Caches store the data in separate regions to isolate different portions of data; such regions are assigned a name, which is useful for configuring each region independently, or to monitor their statistics.

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

By default, entities are cached in regions named after their fully qualified name, e.g. org.acme.Country.

集合缓存到以其所有者实体限定名命名的区域中,该限定名以 #`字符与集合字段名分隔,例如 `org.acme.Country#cities.

Collections are cached in regions named after the fully qualified name of their owner entity and collection field name, separated by # character, e.g. org.acme.Country#cities.

默认情况下,所有缓存查询都会保存在指定的单个区域 `default-query-results-region`中。

All cached queries are by default kept in a single region dedicated to them called default-query-results-region.

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

All regions are bounded by size and time by default. The defaults are 10000 max entries, and 100 seconds as maximum idle time.

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

The size of each region can be customized via the quarkus.hibernate-orm.cache."<region_name>".memory.object-count property (Replace <region_name> with the actual region name).

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

To set the maximum idle time, provide the duration (see note on duration’s format below) via the quarkus.hibernate-orm.cache."<region_name>".expiration.max-idle property (Replace <region_name> with the actual region name).

如果你的区域名称包含一个点,则必须使用双引号。例如:

The double quotes are mandatory if your region name contains a dot. For instance:

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

Unresolved directive in hibernate-orm.adoc - include::{includes}/duration-format-note.adoc[]

Limitations of Caching

目前 Quarkus 提供的缓存技术相当基础,限制也很大。

The caching technology provided within Quarkus is currently quite rudimentary and limited.

团队认为,从一开始就拥有 some 缓存功能总比一无所获要好;您可以期待在将来的版本中整合更好的缓存解决方案,并且非常欢迎在此领域提供任何帮助和反馈。

The team thought it was better to have some caching capability to start with, than having nothing; you can expect better caching solution to be integrated in future releases, and any help and feedback in this area is very welcome.

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

These caches are kept locally, so they are not invalidated or updated when changes are made to the persistent store by other applications.

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

Also, when running multiple copies of the same application (in a cluster, for example on Kubernetes/OpenShift), caches in separate copies of the application aren’t synchronized.

由于这些原因,只有在可以做出某些假设时才适合启用缓存:我们强烈建议只缓存从不更改的实体、集合和查询。或者至多是,当此类实体确实已变异并允许过时(陈旧)读取时,这不会影响应用程序的预期。

For these reasons, enabling caching is only suitable when certain assumptions can be made: we strongly recommend that only entities, collections and queries which never change are cached. Or at most, that when indeed such an entity is mutated and allowed to be read out of date (stale) this has no impact on the expectations of the application.

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

Following this advice guarantees applications get the best performance out of the second-level cache and yet avoid unexpected behaviour.

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

On top of immutable data, in certain contexts it might be acceptable to enable caching also on mutable data; this could be a necessary tradeoff on selected entities which are read frequently and for which some degree of staleness is acceptable; this " acceptable degree of staleness" can be tuned by setting eviction properties. This is however not recommended and should be done with extreme care, as it might produce unexpected and unforeseen effects on the data.

与其在可变数据上启用缓存,理想情况下更好的解决方案是使用群集缓存;然而在目前,Quarkus 没有提供此类实现:随时联系我们并告知此需求,以便团队能够考虑这一点。

Rather than enabling caching on mutable data, ideally a better solution would be to use a clustered cache; however at this time Quarkus doesn’t provide any such implementation: feel free to get in touch and let this need known so that the team can take this into account.

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

Finally, the second-level cache can be disabled globally by setting hibernate.cache.use_second_level_cache to false; this is a setting that needs to be specified in the persistence.xml configuration file.

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

When second-level cache is disabled, all cache annotations are ignored and all queries are run ignoring caches; this is generally useful only to diagnose issues.

Hibernate Envers

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

The Envers extension to Hibernate ORM aims to provide an easy auditing / versioning solution for entity classes.

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

In Quarkus, Envers has a dedicated Quarkus Extension io.quarkus:quarkus-hibernate-envers; you just need to add this to your project to start using it.

Additional dependency to enable Hibernate Envers
    <!-- Add the Hibernate Envers extension -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-envers</artifactId>
    </dependency>

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

At this point the extension does not expose additional configuration properties.

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

For more information about Hibernate Envers, see hibernate.org/orm/envers/.

Metrics

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

Either Micrometer or SmallRye Metrics are capable of exposing metrics that Hibernate ORM collects at runtime. To enable exposure of Hibernate metrics on the /q/metrics endpoint, make sure your project depends on a metrics extension and set the configuration property quarkus.hibernate-orm.metrics.enabled to true. When using SmallRye Metrics, metrics will be available under the vendor scope.

Limitations and other things you should know

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

Quarkus does not modify the libraries it uses; this rule applies to Hibernate ORM as well: when using this extension you will mostly have the same experience as using the original library.

但虽然它们共享相同的代码,但 Quarkus 确实会自动配置某些组件,并为某些扩展点注入自定义实现;这应该是透明且有用的,但如果您是 Hibernate 的专家,您可能希望了解正在执行的操作。

But while they share the same code, Quarkus does configure some components automatically and injects custom implementations for some extension points; this should be transparent and useful but if you’re an expert of Hibernate you might want to know what is being done.

Automatic build time enhancement

Hibernate ORM 可以使用构建时增强的实体;通常这不是强制性的,但它非常有用且可以让您的应用程序性能表现得更好。

Hibernate ORM can use build time enhanced entities; normally this is not mandatory, but it’s useful and will have your applications perform better.

通常,您需要调整构建脚本以包含 Hibernate 增强插件;在 Quarkus 中,由于增强步骤已集成到 Quarkus 应用程序的构建和分析中,因此无需进行此操作。

Typically, you would need to adapt your build scripts to include the Hibernate Enhancement plugins; in Quarkus this is not necessary as the enhancement step is integrated in the build and analysis of the Quarkus application.

由于使用增强,目前不支持在实体上使用 @[1] 方法,因为它还会克隆一些特定于实体的增强特定字段。

Due to the usage of enhancement, using the clone() method on entities is currently not supported as it will also clone some enhancement-specific fields that are specific to the entity.

此限制可能在将来移除。

This limitation might be removed in the future.

Automatic integration

Transaction Manager integration

You don’t need to set this up, Quarkus automatically injects the reference to the Narayana Transaction Manager. The dependency is included automatically as a transitive dependency of the Hibernate ORM extension. All configuration is optional; for more details see Using Transactions in Quarkus.

Connection pool

Don’t need to choose one either. Quarkus automatically includes the Agroal connection pool; configure your datasource as in the above examples and it will set up Hibernate ORM to use Agroal. More details about this connection pool can be found in Quarkus - Datasources.

Second Level Cache

As explained earlier in the caching, you don’t need to pick an implementation. A suitable implementation based on technologies from Infinispan and Caffeine is included as a transitive dependency of the Hibernate ORM extension, and automatically integrated during the build.

Limitations

XML mapping with duplicate files in the classpath

xml-mapping files are expected to have a unique path.

实际上,只有在非常特殊的情况下,才可以在类路径中拥有重复的 XML 映射文件。例如,如果两个 JAR 包含一个 @[4] 文件(具有完全相同的路径,但位于不同的 JAR 中),则映射文件路径 @[5] 只能从 @[6]@[3] 引用。

In practice, it’s only possible to have duplicate XML mapping files in the classpath in very specific scenarios. For example, if two JARs include a META-INF/orm.xml file (with the exact same path but in different JARs), then the mapping file path META-INF/orm.xml can only be referenced from a persistence.xml in the same JAR as the META-INF/orm.xml file.

JMX

Management beans are not working in GraalVM native images; therefore, Hibernate’s capability to register statistics and management operations with the JMX bean is disabled when compiling into a native image. This limitation is likely permanent, as it’s not a goal for native images to implement support for JMX. All such metrics can be accessed in other ways.

JACC Integration

Hibernate ORM’s capability to integrate with JACC is disabled when building GraalVM native images, as JACC is not available - nor useful - in native mode.

Binding the Session to ThreadLocal context

It is impossible to use the ThreadLocalSessionContext helper of Hibernate ORM as support for it is not implemented. Since Quarkus provides out-of-the-box CDI support, injection or programmatic CDI lookup is a better approach. This feature also didn’t integrate well with reactive components and more modern context propagation techniques, making us believe this legacy feature has no future. If you badly need to bind it to a ThreadLocal, it should be trivial to implement in your own code.

JNDI

The JNDI technology is commonly used in other runtimes to integrate different components. A common use case is Java Enterprise servers to bind the TransactionManager and the Datasource components to a name and then have Hibernate ORM configured to look these components up by name. But in Quarkus, this use case doesn’t apply as components are injected directly, making JNDI support an unnecessary legacy. To avoid unexpected use of JNDI, full support for JNDI has been disabled in the Hibernate ORM extension for Quarkus. This is both a security precaution and an optimization.

Other notable differences

Format of import.sql

When importing a import.sql to set up your database, keep in mind that Quarkus reconfigures Hibernate ORM so to require a semicolon (;) to terminate each statement. The default in Hibernate is to have a statement per line, without requiring a terminator other than newline: remember to convert your scripts to use the ; terminator character if you’re reusing existing scripts. This is useful so to allow multi-line statements and human friendly formatting.

Simplifying Hibernate ORM with Panache

@[7] 扩展通过提供活动记录样式实体(和存储库)来促进 Hibernate ORM 的使用,其重点是让实体在 Quarkus 中变得简单易写。

The Hibernate ORM with Panache extension facilitates the usage of Hibernate ORM by providing active record style entities (and repositories) and focuses on making your entities trivial and fun to write in Quarkus.

Configure your datasource

数据源配置非常简单,但它包含在一个不同的指南中,因为从技术上来说它是由 Quarkus 的 Agroal 连接池扩展实现的。

Datasource configuration is extremely simple, but is covered in a different guide as technically it’s implemented by the Agroal connection pool extension for Quarkus.

跳转至 @[8] 了解所有详细信息。

Jump over to Quarkus - Datasources for all details.

Multitenancy

“术语多租户通常适用于软件开发,用于表示应用程序的单一运行实例同时为多个客户端(租户)提供服务的一种架构。这在 SaaS 解决方案中非常常见。隔离与各个租户有关的信息(数据、自定义项等)是这些系统中的一个特殊挑战。其中包括存储在数据库中的由各个租户拥有的数据”(@[9])。

"The term multitenancy, in general, is applied to software development to indicate an architecture in which a single running instance of an application simultaneously serves multiple clients (tenants). This is highly common in SaaS solutions. Isolating information (data, customizations, etc.) pertaining to the various tenants is a particular challenge in these systems. This includes the data owned by each tenant stored in the database" (Hibernate User Guide).

Quarkus 目前支持 @[10] 方法、@[11] 方法和 @[12] 方法。

Quarkus currently supports the separate database approach, the separate schema approach and the discriminator approach.

要查看多租户的运行情况,您可以查看 @[13] 快速入门。

To see multitenancy in action, you can check out the hibernate-orm-multi-tenancy-quickstart quickstart.

Writing the application

让我们从实现 @[14] 端点开始。从下面的源代码中可以看到,它只是一个常规的 Jakarta REST 资源:

Let’s start by implementing the /{tenant} endpoint. As you can see from the source code below it is just a regular Jakarta REST resource:

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

}

为了从传入请求中解决租户并将它映射到特定的租户配置,您需要为 @[15] 接口创建实现。

In order to resolve the tenant from incoming requests and map it to a specific tenant configuration, you need to create an implementation for the io.quarkus.hibernate.orm.runtime.tenant.TenantResolver interface.

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 Annotate the TenantResolver implementation with the @PersistenceUnitExtension qualifier to tell Quarkus it should be used in the default persistence unit.[.iokays-translated-01d73f0e859568d4b5734a5baf5fa1c7] 对于 @[18],使用 @[17]

For multiple-persistence-units, use @PersistenceUnitExtension("nameOfYourPU"). <1> The bean is made @RequestScoped as the tenant resolution depends on the incoming request.

从上面的实现来看,租户是从请求路径解决的,这样即使无法推断出租户,也会返回默认租户标识符。

From the implementation above, tenants are resolved from the request path so that in case no tenant could be inferred, the default tenant identifier is returned.

如果您还使用 @[22],并且 OIDC 和 Hibernate ORM 租户 ID 相同,并且必须从 Vert.x @[20] 中提取,则您可以将租户 ID 从 OIDC Tenant Resolver 传递给 Hibernate ORM Tenant Resolver,作为 @[21] 属性,例如:

If you also use OIDC multitenancy and both OIDC and Hibernate ORM tenant IDs are the same and must be extracted from the Vert.x RoutingContext then you can pass the tenant id from the OIDC Tenant Resolver to the Hibernate ORM Tenant Resolver as a RoutingContext attribute, for example:

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

@PersistenceUnitExtension
@RequestScoped
public class CustomTenantResolver implements TenantResolver {

    @Inject
    RoutingContext context;
    ...
    @Override
    public String resolveTenantId() {
        // OIDC TenantResolver has already calculated the tenant id and saved it as a RoutingContext `tenantId` attribute:
        return context.get("tenantId");
    }
}

Configuring the application

一般来说,Hibernate ORM 数据库生成功能无法与多租户设置结合使用。因此,必须禁用该功能,并确保按架构创建表。以下设置将使用 Flyway 扩展来实现此目标。

In general, it is not possible to use the Hibernate ORM database generation feature in conjunction with a multitenancy setup. Therefore, you have to disable it, and you need to make sure that the tables are created per schema. The following setup will use the Flyway extension to achieve this goal.

SCHEMA approach

所有租户将使用相同的数据源,并且必须在该数据源内为每个租户创建架构。

The same data source will be used for all tenants and a schema has to be created for every tenant inside that data source.

MariaDB/MySQL 等某些数据库不支持数据库架构。在这些情况下,必须使用 database approach

Some databases like MariaDB/MySQL do not support database schemas. In these cases you have to use the database-approach.

quarkus.hibernate-orm.database.generation=none 1

quarkus.hibernate-orm.multitenant=SCHEMA 2

quarkus.datasource.db-kind=postgresql 3
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test

quarkus.flyway.schemas=base,mycompany 4
quarkus.flyway.locations=classpath:schema
quarkus.flyway.migrate-at-start=true
1 Disable schema generation, because it is not supported by Hibernate ORM for schema multi-tenancy. We’ll use Flyway instead, see further down.
2 Enable schema multi-tenancy.[.iokays-translated-5ab933955255e5213ef72035c048c4e5] 我们在此使用默认数据源,但如果愿意,可以通过按照 there 中的说明来使用已命名的数据源。

We use the default datasource here, but could use a named datasource if we wanted to, by following instructions multiple-persistence-units. <1> Configure the datasource. <1> Configure Flyway for database initialization, because schema generation by Hibernate ORM is not supported in this case.

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

Here is an example of the Flyway SQL (V1.0.0__create_fruits.sql) to be created in the configured folder src/main/resources/schema.

CREATE SEQUENCE base.known_fruits_id_seq;
SELECT setval('base."known_fruits_id_seq"', 3);
CREATE TABLE base.known_fruits
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO base.known_fruits(id, name) VALUES (1, 'Cherry');
INSERT INTO base.known_fruits(id, name) VALUES (2, 'Apple');
INSERT INTO base.known_fruits(id, name) VALUES (3, 'Banana');

CREATE SEQUENCE mycompany.known_fruits_id_seq;
SELECT setval('mycompany."known_fruits_id_seq"', 3);
CREATE TABLE mycompany.known_fruits
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO mycompany.known_fruits(id, name) VALUES (1, 'Avocado');
INSERT INTO mycompany.known_fruits(id, name) VALUES (2, 'Apricots');
INSERT INTO mycompany.known_fruits(id, name) VALUES (3, 'Blackberries');

DATABASE approach

对于每个租户,都需要创建一个命名的数据源,其中标识符由 TenantResolver 返回。

For every tenant you need to create a named data source with the same identifier that is returned by the TenantResolver.

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

With this approach, all datasources used by the same persistence unit are assumed to point to a database of the same vendor (same db-kind) and version.

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

Mismatches will not be detected, and may result in unpredictable behavior.

quarkus.hibernate-orm.database.generation=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.datasource.base.username=quarkus_test
quarkus.datasource.base.password=quarkus_test
quarkus.datasource.base.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test
quarkus.flyway.base.locations=classpath:database/base 5
quarkus.flyway.base.migrate-at-start=true

# Tenant 'mycompany'
quarkus.datasource.mycompany.db-kind=postgresql 6
quarkus.datasource.mycompany.username=mycompany
quarkus.datasource.mycompany.password=mycompany
quarkus.datasource.mycompany.jdbc.url=jdbc:postgresql://localhost:5433/mycompany
quarkus.flyway.mycompany.locations=classpath:database/mycompany 7
quarkus.flyway.mycompany.migrate-at-start=true
1 Disable schema generation, because it is not supported by Hibernate ORM for database multi-tenancy. We’ll use Flyway instead, see further down.
2 Enable database multi-tenancy.
3 Select a datasource for the persistence unit.[.iokays-translated-e947aafb4cb667d214678dc10b296bac] 这只是为了允许 Quarkus 确定要使用的 Hibernate ORM 方言;有关详细信息,请参见 this section

This is only to allow Quarkus to determine the Hibernate ORM dialect to use; see hibernate-dialect-varying-database for details. <1> Configure the datasource for one tenant, base. <1> Configure Flyway for database initialization for tenant base, because schema generation by Hibernate ORM is not supported in this case. <1> Configure the datasource for another tenant.[.iokays-translated-33beeaf6cefb39be84c512274e80bff5] 可能还有更多租户,但这里我们停在两个。

There could be more tenants, but here we’re stopping at two. <1> Configure Flyway for database initialization for tenant mycompany, because schema generation by Hibernate ORM is not supported in this case.

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

Following are examples of the Flyway SQL files to be created in the configured folder src/main/resources/database.

base (src/main/resources/database/base/V1.0.0__create_fruits.sql) 租户架构:

Schema for tenant base (src/main/resources/database/base/V1.0.0__create_fruits.sql):

CREATE SEQUENCE known_fruits_id_seq;
SELECT setval('known_fruits_id_seq', 3);
CREATE TABLE known_fruits
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO known_fruits(id, name) VALUES (1, 'Cherry');
INSERT INTO known_fruits(id, name) VALUES (2, 'Apple');
INSERT INTO known_fruits(id, name) VALUES (3, 'Banana');

mycompany (src/main/resources/database/mycompany/V1.0.0__create_fruits.sql) 租户架构:

Schema for tenant mycompany (src/main/resources/database/mycompany/V1.0.0__create_fruits.sql):

CREATE SEQUENCE known_fruits_id_seq;
SELECT setval('known_fruits_id_seq', 3);
CREATE TABLE known_fruits
(
  id   INT,
  name VARCHAR(40)
);
INSERT INTO known_fruits(id, name) VALUES (1, 'Avocado');
INSERT INTO known_fruits(id, name) VALUES (2, 'Apricots');
INSERT INTO known_fruits(id, name) VALUES (3, 'Blackberries');

DISCRIMINATOR approach

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

The default data source will be used for all tenants. All entities defining a field annotated with @TenantId will have that field populated automatically, and will get filtered automatically in queries.

quarkus.hibernate-orm.multitenant=DISCRIMINATOR 1

quarkus.datasource.db-kind=postgresql 2
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test
1 Enable discriminator multi-tenancy.
2 Configure the datasource.

Programmatically Resolving Tenants Connections

如果您需要对想要支持的不同租户使用更动态的配置并且不想在配置文件中结束多个条目,那么可以使用 io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver 接口实现自己的逻辑来检索连接。创建一个实现了此接口且用 @PersistenceUnitExtension 注释的应用程序范围 bean(或者用 @PersistenceUnitExtension("nameOfYourPU") 注释,对于 named persistence unit),将替换当前 Quarkus 默认实现 io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver。自定义连接解析器可以例如从数据库中读取租户信息并根据此信息在运行时为每个租户创建一个连接。

If you need a more dynamic configuration for the different tenants you want to support and don’t want to end up with multiple entries in your configuration file, you can use the io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver interface to implement your own logic for retrieving a connection. Creating an application-scoped bean that implements this interface and annotating it with @PersistenceUnitExtension (or @PersistenceUnitExtension("nameOfYourPU") for a multiple-persistence-units) will replace the current Quarkus default implementation io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver. Your custom connection resolver would allow for example to read tenant information from a database and create a connection per tenant at runtime based on it.

Interceptors

您可以通过使用适当限定符定义 CDI Bean 来为 SessionFactory 分配 org.hibernate.Interceptor

You can assign an org.hibernate.Interceptor to your SessionFactory by simply defining a CDI bean with the appropriate qualifier:

@PersistenceUnitExtension (1)
public static class MyInterceptor extends EmptyInterceptor { (2)
    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state, (3)
            String[] propertyNames, Type[] types) {
        // ...
        return false;
    }
}
1 Annotate the interceptor implementation with the @PersistenceUnitExtension qualifier to tell Quarkus it should be used in the default persistence unit.[.iokays-translated-26b7e3d513a2d701019fd50658387583] 对于 named persistence units,请使用 @PersistenceUnitExtension("nameOfYourPU")

For multiple-persistence-units, use @PersistenceUnitExtension("nameOfYourPU") <1> Either extend org.hibernate.EmptyInterceptor or implement org.hibernate.Interceptor directly. <1> Implement methods as necessary.

默认情况下,使用 @PersistenceUnitExtension 注释的拦截器 bean 是应用程序范围的,这意味着每个应用程序只创建一个拦截器实例,并在所有实体管理器中重复使用。由于此原因,拦截器实现必须是线程安全的。

By default, interceptor beans annotated with @PersistenceUnitExtension are application-scoped, which means only one interceptor instance will be created per application and reused across all entity managers. For this reason, the interceptor implementation must be thread-safe.

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

In order to create one interceptor instance per entity manager instead, annotate your bean with @Dependent. In that case, the interceptor implementation doesn’t need to be thread-safe.

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

Due to a limitation in Hibernate ORM itself, @PreDestroy methods on @Dependent-scoped interceptors will never get called.

Statement Inspectors

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

You can assign a org.hibernate.engine.jdbc.spi.StatementInspector to your SessionFactory by simply defining a CDI bean with the appropriate qualifier:

@PersistenceUnitExtension (1)
public class MyStatementInspector implements StatementInspector { (2)
    @Override
    public String inspect(String sql) {
        // ...
        return sql;
    }
}
1 Annotate the statement inspector implementation with the @PersistenceUnitExtension qualifier to tell Quarkus it should be used in the default persistence unit.[.iokays-translated-26b7e3d513a2d701019fd50658387583] 对于 named persistence units,请使用 @PersistenceUnitExtension("nameOfYourPU")

For multiple-persistence-units, use @PersistenceUnitExtension("nameOfYourPU") <1> Implement org.hibernate.engine.jdbc.spi.StatementInspector.

Customizing JSON/XML serialization/deserialization

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

By default, Quarkus will try to automatically configure format mappers depending on available extensions. Globally configured ObjectMapper (or Jsonb) will be used for serialization/deserialization operations when Jackson (or JSON-B) is available. Jackson will take precedence if both Jackson and JSON-B are available at the same time.

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

JSON and XML serialization/deserialization in Hibernate ORM can be customized by implementing a org.hibernate.type.format.FormatMapper and annotating the implementation with the appropriate qualifiers:

@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 Annotate the format mapper implementation with the @JsonFormat qualifier to tell Quarkus that this mapper is specific to JSON serialization/deserialization.
2 Annotate the format mapper implementation with the @PersistenceUnitExtension qualifier to tell Quarkus it should be used in the default persistence unit.[.iokays-translated-26b7e3d513a2d701019fd50658387583] 对于 named persistence units,请使用 @PersistenceUnitExtension("nameOfYourPU")

For multiple-persistence-units, use @PersistenceUnitExtension("nameOfYourPU") <1> Implement org.hibernate.type.format.FormatMapper.

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

In case of a custom XML format mapper, a different CDI qualifier must be applied:

@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 Annotate the format mapper implementation with the @XmlFormat qualifier to tell Quarkus that this mapper is specific to XML serialization/deserialization.
2 Annotate the format mapper implementation with the @PersistenceUnitExtension qualifier to tell Quarkus it should be used in the default persistence unit.[.iokays-translated-26b7e3d513a2d701019fd50658387583] 对于 named persistence units,请使用 @PersistenceUnitExtension("nameOfYourPU")

For multiple-persistence-units, use @PersistenceUnitExtension("nameOfYourPU") <1> Implement org.hibernate.type.format.FormatMapper.

格式映射器 *must*既拥有 `@PersistenceUnitExtension`限定符,又拥有 `@JsonFormat`或 `@XmlFormat`CDI 限定符。

Format mappers must have both @PersistenceUnitExtension and either @JsonFormat or @XmlFormat CDI qualifiers applied.

因为会产生歧义,所以如果为同一持久性单元注册了多个 JSON(或 XML)格式映射器,则会引发异常。

Having multiple JSON (or XML) format mappers registered for the same persistence unit will result in an exception, because of the ambiguity.