Quarkus - 使用 Hibernate ORM 和 JPA

Hibernate ORM 是事实上的 JPA 实现标准,可提供对象关系映射的完整功能。它在Quarkus 中可完美运行。

设置和配置 Hibernate ORM

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

也可以使用这样的经典配置文件,但是除非您有特定的高级需求,否则不必这样做。 因此,我们首先将了解下不用 persistence.xml 怎么配置 Hibernate ORM 。

在 Quarkus 中,您只需要:

  • application.properties 中添加配置

  • 像往常一样,使用 @Entity 和其他任何 JPA 注解来注释实体类

其他需要的设置需求已经自动配置:Quarkus 将做出一些明智的选择和有根据的猜测。

将以下依赖项添加到项目中:

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

  • JDBC 驱动 extension ; 可从下列中选用:

使用 Maven 添加依赖项示例
<dependencies>
    <!-- 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>
</dependencies>

@Entity 注解持久对象,并在 application.properties 中添加相关配置属性

application.properties 示例
# datasource 配置
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = hibernate
quarkus.datasource.password = hibernate
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db

# 启动时删除并重建数据库 (如果只需更新数据库结构可以用 `update`)
quarkus.hibernate-orm.database.generation=drop-and-create

请注意,这些配置属性与典型的 Hibernate ORM 配置文件中的属性不同:这些是给 Quarkus 用的配置属性,它们通常会映射到 Hibernate 配置属性,但名称可能不同,不一定彼此之间一一对应。

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

application.properties 中可以配置的属性列表请参阅下面的 Hibernate ORM configuration properties 部分。

项目一旦依赖了 Hibernate ORM extension ,Quarkus 就会根据 datasource 配置创建一个 EntityManagerFactory

将基于 JDBC 驱动自动选择数据库方言(dialect) - 除非明确指定。

然后,可以愉快地注入 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 注入 entity manager 就可以用了
2 将 CDI bean 的方法注解上 @TransactionalEntityManager 会暂存修改并在提交事务时保存.
Entity 示例
@Entity
public class Gift {
    private Long id;
    private String name;

    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, 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 语句,需要在 resource 根目录中添加 import.sql 文件。 此文件中可以包含任何 SQL DML 语句;确保以分号终止每个语句。 这对测试或演示准备好数据集很有用。

确保将修改数据库(例如 entity.persist())的方法包在事务内。将 CDI bean 方法注解上 @Transactional 能做到这一点,并使该方法成为事务边界。我们建议您在应用程序入口点边界(例如REST端点控制器)这样做。

Hibernate ORM 配置属性

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

只要配置了默认数据源(datasource),就没有额外必需的属性需要配置。

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

此处列出的配置属性让你覆盖默认设置,可自定义和调整各个方面。

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

Class name of the Hibernate ORM dialect. The complete list of bundled dialects is available in the Hibernate ORM JavaDoc.

Not all the dialects are supported in GraalVM native executables: we currently provide driver extensions for PostgreSQL, MariaDB, Microsoft SQL Server and H2.

string

The storage engine to use when the dialect supports multiple storage engines.

E.g. MyISAM or InnoDB for MySQL.

string

Name of the file containing the SQL statements to execute when Hibernate ORM starts. Its default value differs depending on the Quarkus launch mode:

  • In dev and test modes, it defaults to import.sql. Simply add an import.sql file in the root of your resources directory and it will be picked up without having to set this property. Pass no-file to force Hibernate ORM to ignore the SQL import file.

  • In production mode, it defaults to no-file. It means Hibernate ORM won’t try to execute any SQL import file by default. Pass an explicit value to force Hibernate ORM to execute the SQL import file.

If you need different SQL statements between dev mode, test (@QuarkusTest) and in production, use Quarkus configuration profiles facility.

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 supports .sql file with SQL statements or comments spread over multiple lines. Each SQL statement must be terminated by a semicolon.

string

import.sql in DEV, TEST ; no-file otherwise

The size of the batches used when loading entities and collections.

-1 means batch loading is disabled. This is the default.

int

-1

Pluggable strategy contract for applying physical naming rules for database object names. Class name of the Hibernate PhysicalNamingStrategy implementation

string

Pluggable strategy for applying implicit naming rules when an explicit name is not given. Class name of the Hibernate ImplicitNamingStrategy implementation

string

Whether statistics collection is enabled. If 'metrics.enabled' is true, then the default here is considered true, otherwise the default is false.

boolean

Whether or not metrics are published in case the smallrye-metrics extension is present (default to false).

boolean

false

The default in Quarkus is for 2nd level caching to be enabled, and a good implementation is already integrated for you. Just cherry-pick which entities should be using the cache. Set this to false to disable all 2nd level caches.

boolean

true

Query related configuration

Type

Default

The maximum size of the query plan cache.

string

Default precedence of null values in ORDER BY clauses.

Valid values are: none, first, last.

string

Database related configuration

Type

Default

Select whether the database schema is generated or not. drop-and-create is awesome in development mode. Accepted values: none, create, drop-and-create, drop, update.

string

none

Whether we should stop on the first error when applying the schema.

boolean

false

The default catalog to use for the database objects.

string

The default schema to use for the database objects.

string

The charset of the database.

string

Whether Hibernate should quote all identifiers.

boolean

false

JDBC related configuration

Type

Default

The time zone pushed to the JDBC driver.

string

How many rows are fetched at a time by the JDBC driver.

int

The number of updates (inserts, updates and deletes) that are sent by the JDBC driver at one time for execution.

int

Logging configuration

Type

Default

Show SQL logs and format them nicely. Setting it to true is obviously not recommended in production.

boolean

false

Whether JDBC warnings should be collected and logged.

boolean

depends on dialect

Caching configuration

Type

Default

The maximum time before an object of the cache is considered expired.

Duration

The maximum number of objects kept in memory in the cache.

long

About the Duration format

The format for durations uses the standard java.time.Duration format. You can learn more about it in the Duration#parse() javadoc.

You can also provide duration values starting with a number. In this case, if the value consists only of a number, the converter treats the value as seconds. Otherwise, PT is implicitly prepended to the value to obtain a standard java.time.Duration format.

Do not mix 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.

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

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 \
           --name postgres-quarkus-hibernate -e POSTGRES_USER=hibernate \
           -e POSTGRES_PASSWORD=hibernate -e POSTGRES_DB=hibernate_db \
           -p 5432:5432 postgres:10.5

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

使用 persistence.xml 配置 Hibernate ORM

另外,您可以使用 META-INF/persistence.xml 来设置 Hibernate ORM 。 主要用于:

  • 迁移现有代码

  • 当您具有相对复杂的设置,需要完全灵活的配置时

  • 或者如果您更喜欢老方式

如果用了 persistence.xml ,则不能用 quarkus.hibernate-orm.* 这些属性, 并且只有 persistence.xml 定义的持久性单元(persistence units )才有效。

pom.xml 中的依赖以及 Java 代码跟前面的示例相同。 唯一的区别是,您将在 META-INF/persistence.xml 配置 Hibernate ORM :

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.PostgreSQL95Dialect"/>

            <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="javax.persistence.schema-generation.database.action" value="drop-and-create"/>

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

    </persistence-unit>
</persistence>

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

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

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

Quarkus 中的 Hibernate ORM 需要编译时对实体类字节码增强。 如果您在创建 Quarkus 应用程序的同一项目中定义实体,那么一切都会正常运行。

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

这将使 Quarkus 把它们当作在当前项目中一样索引并增强实体类。

开发模式下的Hibernate ORM

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

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

首选是与 quarkus.hibernate-orm.database.generation=drop-and-create 结合使用 import.sql

这样,对于您的应用程序(尤其是实体)的每次更改,都将正确地重新创建数据库表, 并将使数据从头初始化(存储在中 import.sql)。 这完美的控制环境,在 Quarkus 实时重新加载模式下也能使用: 修改实体 或 import.sql 都无需重新启动应用程序自动更新表!

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

第二种方法是使用 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. 当您在生产数据的副本上工作但想完全控制架构演变时,这种方法是最好的。 或者,如果您使用 Flyway 之类的数据库迁移工具。

使用这种方法对实体进行更改时,请确保相应地调整数据库表。 您还可以使用 validate 让 Hibernate 验证数据库表结构是否符合。

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

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

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
Start "dev mode" using a custom profile via Maven
./mvnw compile quarkus:dev -Dquarkus.profile=dev-with-data

生产模式下的 Hibernate ORM

Quarkus 自带的默认配置文件(dev, testprod)。你也可以添加自己的自定义配置文件来描述各种环境(staging, prod-us,等)。

Hibernate ORM Quarkus extension 在 devtest 模式下的一些默认配置与在其他环境中不同。

  • devtest 之外的配置环境 quarkus.hibernate-orm.sql-load-script 都配置成了 no-file

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

确保生产环境中避免删除数据库架构! 可在属性文件中添加以下内容。

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

缓存

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

缓存 entities

要启用二级缓存,请在要缓存的实体上注解 @javax.persistence.Cacheable

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

当实体类注解了 @Cacheable 后,将缓存其所有字段值(集合和与其他实体的关系除外)。

这意味着可以在不查询数据库的情况下加载该实体,但是要小心, 因为它暗示所加载的实体可能不会反映数据库中的最新更改。

缓存集合和关联

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: 集合和关联需要单独注解才会进行缓存; 这时,可使用 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 中已经默认集成了缓存技术并启用了缓存技术,因此足以设置哪些缓存是安全的。

调整缓存区域

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.

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

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.

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

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

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).

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

The format for durations uses the standard java.time.Duration format. You can learn more about it in the Duration#parse() javadoc.

You can also provide duration values starting with a number. In this case, if the value consists only of a number, the converter treats the value as seconds. Otherwise, PT is implicitly prepended to the value to obtain a standard java.time.Duration format.

缓存的局限性

Quarkus 中提供的缓存技术目前还很初级且有限。

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.

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.

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.

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.

指标

The SmallRye Metrics extension is capable of exposing metrics that Hibernate ORM collects at runtime. To enable exposure of Hibernate metrics on the /metrics endpoint, make sure your project depends on the quarkus-smallrye-metrics artifact and set the configuration property quarkus.hibernate-orm.metrics.enabled to true. Metrics will then be available under the vendor scope.

局限和其它

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.

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 can use build time enhanced entities; normally this is not mandatory but it’s useful and will have your applications perform better.

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.

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; just configure your datasource as in the above examples and it will setup Hibernate ORM to use Agroal. More details about this connection pool can be found in Quarkus - Datasources.

Second Level Cache

as explained above in section 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

Hibernate ORM allows to map entities using XML files; this capability isn’t enabled in Quarkus: use annotations instead as Quarkus can handle them very efficiently. This limitation could be lifted in the future, if there’s a compelling need for it and if someone contributes it.

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

Essentially using the ThreadLocalSessionContext helper of Hibernate ORM is not implemented. The team believes this isn’t a big deal as it’s trivial to inject the Session via CDI instead, or handling the binding into a ThreadLocal yourself, making this a legacy feature. This limitation might be resolved in the future, if someone opens a ticket for it and provides a reasonable use case to justify the need.

JPA Callbacks

Annotations allowing for application callbacks on entity lifecycle events defined by JPA such as @javax.persistence.PostUpdate, @javax.persistence.PostLoad, @javax.persistence.PostPersist, etc…​ are currently not processed. This limitation could be resolved in a future version, depending on user demand.

Single instance

It is currently not possible to configure more than one instance of Hibernate ORM. This is a temporary limitation, the team is working on it - please be patient!

Other notable differences

Format of import.sql

When importing a import.sql to setup 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.

用 Panache 简便 Hibernate ORM

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.

配置数据源(datasource)

数据源配置非常简单,但是在技术上是由 Quarkus Agroal 连接池 extension 实现的,因此在另一本指南中进行了介绍。

quarkus.pro 是基于 quarkus.io 的非官方中文翻译站 ,最后更新 2020/04 。
沪ICP备19006215号-8
QQ交流群:1055930959
微信群: