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 ; 可从下列中选用:
-
quarkus-jdbc-derby
用于 Apache Derby -
quarkus-jdbc-h2
用于 H2 -
quarkus-jdbc-mariadb
用于 MariaDB -
quarkus-jdbc-mssql
用于 Microsoft SQL Server -
quarkus-jdbc-mysql
用于 MySQL -
quarkus-jdbc-postgresql
用于 PostgreSQL
-
<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
:
@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 的方法注解上 @Transactional 后 EntityManager 会暂存修改并在提交事务时保存. |
@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
Type |
Default |
|||
---|---|---|---|---|
Class name of the Hibernate ORM dialect. The complete list of bundled dialects is available in the Hibernate ORM JavaDoc.
|
string |
|||
The storage engine to use when the dialect supports multiple storage engines. E.g. |
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:
If you need different SQL statements between dev mode, test ( application.properties
|
string |
|
||
The size of the batches used when loading entities and collections.
|
int |
|
||
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 |
|
||
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 |
|
||
Type |
Default |
|||
The maximum size of the query plan cache. |
string |
|||
Default precedence of null values in Valid values are: |
string |
|||
Type |
Default |
|||
Select whether the database schema is generated or not. |
string |
|
||
Whether we should stop on the first error when applying the schema. |
boolean |
|
||
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 |
|
||
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 |
|||
Type |
Default |
|||
Show SQL logs and format them nicely. Setting it to true is obviously not recommended in production. |
boolean |
|
||
Whether JDBC warnings should be collected and logged. |
boolean |
|
||
Type |
Default |
|||
The maximum time before an object of the cache is considered expired. |
||||
The maximum number of objects kept in memory in the cache. |
long |
About the Duration format
The format for durations uses the standard 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, |
Do not mix |
Want to start a PostgreSQL server on the side with Docker?
This will start a non-durable empty database: ideal for a quick experiment! |
使用 persistence.xml
配置 Hibernate ORM
另外,您可以使用 META-INF/persistence.xml
来设置 Hibernate ORM 。
主要用于:
-
迁移现有代码
-
当您具有相对复杂的设置,需要完全灵活的配置时
-
或者如果您更喜欢老方式
如果用了 |
pom.xml
中的依赖以及 Java 代码跟前面的示例相同。
唯一的区别是,您将在 META-INF/persistence.xml
配置 Hibernate ORM :
<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
都无需重新启动应用程序自动更新表!
默认情况下,在 |
第二种方法是使用 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-create 和 update 。
|
当与 Quarkus 配置文件结合使用时,这些方法将变得非常强大。 您可以定义不同的 配置 profiles , 以根据您的环境选择不同的行为。 这很棒,因为您可以定义Hibernate ORM属性的不同组合,以匹配当前所需的开发形式。
%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
./mvnw compile quarkus:dev -Dquarkus.profile=dev-with-data
生产模式下的 Hibernate ORM
Quarkus 自带的默认配置文件(dev
, test
和 prod
)。你也可以添加自己的自定义配置文件来描述各种环境(staging
, prod-us
,等)。
Hibernate ORM Quarkus extension 在 dev
和 test
模式下的一些默认配置与在其他环境中不同。
-
dev
和test
之外的配置环境quarkus.hibernate-orm.sql-load-script
都配置成了no-file
。
您可以在 application.properties
显式配置它(例如 %prod.quarkus.hibernate-orm.sql-load-script = import.sql
)
但我们希望您避免在 prod
中意外覆盖数据库:)
确保生产环境中避免删除数据库架构! 可在属性文件中添加以下内容。
%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:
|
The format for durations uses the standard 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, |
缓存的局限性
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.