Hibernate ORMとJakarta Persistenceの使用
Hibernate ORMは、Jakarta Persistence(旧称JPA)のデファクトスタンダード実装であり、Object Relational Mapperの完全な実装を提供します。Quarkusで見事に機能します。
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitリポジトリをクローンする: git clone https://github.com/quarkusio/quarkus-quickstarts.git
、または archive をダウンロードする。
ソリューションは hibernate-orm-quickstart
ディレクトリ にあります。
Hibernate ORMのセットアップと設定
QuarkusでHibernate ORMを使用する場合は、 設定の為に persistence.xml
リソースは必要ありません。
このような古典的な設定ファイルを使用することは選択しとしてありますが、特定の高度なニーズがない限り不要です。そのため、まずはHibernate ORMを persistence.xml
リソース無しで設定できることをみていきましょう。
Quarkusでは、次のことを行うだけです:
-
application.properties
に設定を追加します -
エンティティに
@Entity
やその他のマッピングアノテーションを通常通りにアノテーションします
その他の設定の必要性は自動化されています。Quarkusは、いくつかの定見に基づいた選択と経験に基づいた推測を行います。
以下の依存関係をプロジェクトに追加してください:
-
Hibernate ORM エクステンション:
io.quarkus:quarkus-hibernate-orm
-
JDBC ドライバーエクステンション。以下のオプションを使用できます:
-
IBM DB2 のための
quarkus-jdbc-db2
-
Apache Derby のための
quarkus-jdbc-derby
-
H2 のための
quarkus-jdbc-h2
-
MariaDB のための
quarkus-jdbc-mariadb
-
Microsoft SQL Server のための
quarkus-jdbc-mssql
-
MySQL のための
quarkus-jdbc-mysql
-
Oracle Database のための
quarkus-jdbc-oracle
-
PostgreSQL のための
quarkus-jdbc-postgresql
-
例:
<!-- 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>
// Hibernate ORM specific dependencies
implementation("io.quarkus:quarkus-hibernate-orm")
// JDBC driver dependencies
implementation("io.quarkus:quarkus-jdbc-postgresql")
persistent オブジェクトに`@Entity` アノテーションを付けてから、 application.properties
で関連する設定プロパティーを追加します。
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の設定のプロパティに対応していますが、名前が異なる場合もあり、必ずしも1対1で対応しているわけではありません。
また、Quarkusは多くのHibernate ORMの設定を自動的に設定し、多くの場合、より現代的なデフォルト値を使用します。
For a list of the items that you can set in application.properties
, see Hibernate ORM configuration properties.
Hibernate ORM エクステンションがプロジェクトの依存関係の中に入っていればQuarkus の datasource
の設定に基づいて EntityManagerFactory
が作成されます。
The dialect will be selected and configured automatically based on your datasource; you may want to configure it to more precisely match your database.
その後、 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 | エンティティマネージャーをインジェクションして楽しむ |
2 | CDI Beanメソッドに @Transactional を付けると EntityManager がトランザクション境界内に入りコミット時にフラッシュします。 |
@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文をロードするには、 import.sql
ファイルをresourcesディレクトリーのルートに追加します。このスクリプトには、任意のSQL DML文を含めることができます。各ステートメントは必ずセミコロンで終了させてください。
テストやデモ用のデータセットを用意しておくと便利です。
データベースを変更するメソッド (例: entity.persist() ) をトランザクション内でラップするようにしてください。CDI Beanメソッド @Transactional をマークすることで、それを実現出来、そのメソッドをトランザクションの境界に出来ます。REST エンドポイントコントローラーのように、アプリケーションのエントリーポイントの境界でこれを行うことをお勧めします。
|
Dialect
サポートされるデータベース
サポートされているデータベース では、 Hibernate ORM ダイアレクト は明示的に設定する必要はありません。
デフォルトでは、データベースの最小サポートバージョンをターゲットとするようにdialectが設定されています。
Hibernate ORMがより効率的なSQLを生成し、ワークアラウンドを回避し、より多くのデータベース機能を活用するために、データベースバージョンを明示的に設定することができます:
db-version
を明示した application.properties
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 | データベースのバージョンを設定します。Hibernate ORM dialectはそのバージョンをターゲットにします。 |
原則として、ここで設定するバージョンはできるだけ高くする必要がありますが、アプリケーションが接続するデータベースのバージョン以下である必要があります。
As described above, the version can either be preconfigured explicitly via a This is a safeguard: for versions of the database older than what is configured, Hibernate ORM may generate SQL that is invalid which would lead to runtime exceptions. If the database cannot be reached, a warning will be logged but startup will proceed.
You can optionally disable the version check if you know the database won’t be reachable on startup
using |
その他のデータベース
データベースに対応するQuarkusエクステンションモジュールがない 場合や、何らかの理由でデフォルトがニーズに合わない場合は、明示的に Hibernate ORMダイアレクト を設定する必要があります:
dialect
を明示した application.properties
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 | Hibernate ORM dialectを設定します。
For built-in dialects, the expected value is one of the names
in the official list of dialects, without the For third-party dialects, the expected value is the fully-qualified class name,
for example |
この場合、JDBCドライバやHibernate ORM dialectがGraalVMネイティブ実行可能ファイルでは正しく動作しない可能性があることに留意してください。 |
As with supported databases, you can configure the DB version explicitly to get the most out of Hibernate ORM:
dialect
と db-version
を明示した application.properties
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 | データベースのバージョンを設定します。Hibernate ORM dialectはそのバージョンをターゲットにします。ここではCockroachDBをターゲットにしているので、PostgreSQLのバージョンではなく、CockroachDBのバージョンを渡しています。 |
2 | Hibernate ORM dialectを設定します。 |
Varying database
When enabling database multi-tenancy, 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.
For that reason, when enabling database multi-tenancy,
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).
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 this section.
Hibernate ORMの設定プロパティ
EntityManagerFactory
を改良したり、Quarkusの推測を導くのに便利な様々なオプションのプロパティがあります。
デフォルトのデータソースが設定されていれば、それ以外に必須のプロパティはありません。
プロパティが設定されていない場合、Quarkusは通常はHibernate ORMのセットアップに必要な値を推測し、デフォルトのデータソースを使用するようにします。
ここに記載されている設定プロパティでは、このようなデフォルトを上書きしたり、様々な面をカスタマイズしたり調整したりすることができます。
ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは実行時にオーバーライド可能
Configuration property |
型 |
デフォルト |
||
---|---|---|---|---|
Whether Hibernate ORM is enabled during the build. If Hibernate ORM is disabled during the build, all processing related to Hibernate ORM will be skipped,
but it will not be possible to activate Hibernate ORM at runtime:
Environment variable: Show more |
boolean |
|
||
If Environment variable: Show more |
boolean |
|
||
Whether statistics collection is enabled. If 'metrics.enabled' is true, then the default here is considered true, otherwise the default is false. Environment variable: Show more |
boolean |
|||
Whether session metrics should be appended into the server log for each Hibernate session. This only has effect if statistics are enabled ( Environment variable: Show more |
boolean |
|||
Whether metrics are published if a metrics extension is enabled. Environment variable: Show more |
boolean |
|
||
The name of the datasource which this persistence unit uses. If undefined, it will use the default datasource. Environment variable: Show more |
string |
|||
The packages in which the entities affected to this persistence unit are located. Environment variable: Show more |
list of string |
|||
Paths to files containing the SQL statements to execute when Hibernate ORM starts. The files are retrieved from the classpath resources,
so they must be located in the resources directory (e.g. The default value for this setting differs depending on the Quarkus launch mode:
If you need different SQL statements between dev mode, test ( application.properties
Environment variable: Show more |
list of string |
|
||
Pluggable strategy contract for applying physical naming rules for database object names. Class name of the Hibernate PhysicalNamingStrategy implementation Environment variable: Show more |
string |
|||
Pluggable strategy for applying implicit naming rules when an explicit name is not given. Class name of the Hibernate ImplicitNamingStrategy implementation Environment variable: Show more |
string |
|||
Class name of a custom
Environment variable: Show more |
string |
|||
XML files to configure the entity mapping, e.g. Defaults to Environment variable: Show more |
list of string |
|
||
Identifiers can be quoted using one of the available strategies. Set to Environment variable: Show more |
|
|
||
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. Environment variable: Show more |
boolean |
|
||
Enables the Bean Validation integration. Environment variable: Show more |
boolean |
|
||
Defines the method for multi-tenancy (DATABASE, NONE, SCHEMA). The complete list of allowed values is available in the Hibernate ORM JavaDoc. The type DISCRIMINATOR is currently not supported. The default value is NONE (no multi-tenancy). Environment variable: Show more |
string |
|||
If hibernate is not auto generating the schema, and Quarkus is running in development mode then Quarkus will attempt to validate the database after startup and print a log message if there are any problems. Environment variable: Show more |
boolean |
|
||
Whether this persistence unit should be active at runtime. Note that if Hibernate ORM is disabled (i.e. Environment variable: Show more |
boolean |
|
||
Properties that should be passed on directly to Hibernate ORM.
Use the full configuration property key here,
for instance
Consider using a supported configuration property before falling back to unsupported ones. If none exists, make sure to file a feature request so that a supported configuration property can be added to Quarkus, and more importantly so that the configuration property is tested regularly. Environment variable: Show more |
Map<String,String> |
|||
型 |
デフォルト |
|||
When set, attempts to exchange data with the database as the given version of Hibernate ORM would have, on a best-effort basis. Please note:
Environment variable: Show more |
|
|
||
The charset of the database. Used for DDL generation and also for the SQL import scripts. Environment variable: Show more |
|
|||
Select whether the database schema is generated or not. Environment variable: Show more |
string |
|
||
If Hibernate ORM should create the schemas automatically (for databases supporting them). Environment variable: Show more |
boolean |
|
||
Whether we should stop on the first error when applying the schema. Environment variable: Show more |
boolean |
|
||
The default catalog to use for the database objects. Environment variable: Show more |
string |
|||
The default schema to use for the database objects. Environment variable: Show more |
string |
|||
Whether Hibernate ORM should check on startup
that the version of the database matches the version configured on the dialect
(either the default version, or the one set through This should be set to Environment variable: Show more |
boolean |
|
||
型 |
デフォルト |
|||
Name of the Hibernate ORM dialect. For supported databases, this property does not need to be set explicitly: it is selected automatically based on the datasource, and configured using the DB version set on the datasource to benefit from the best performance and latest features. If your database does not have a corresponding Quarkus extension, you will need to set this property explicitly. In that case, keep in mind that the JDBC driver and Hibernate ORM dialect may not work properly in GraalVM native executables. For built-in dialects, the expected value is one of the names
in the official list of dialects,
without the For third-party dialects, the expected value is the fully-qualified class name,
for example Environment variable: Show more |
string |
|
||
The storage engine to use when the dialect supports multiple storage engines. E.g. Environment variable: Show more |
string |
|||
型 |
デフォルト |
|||
How to store timezones in the database by default
for properties of type This default may be overridden on a per-property basis using
Environment variable: Show more |
|
|
||
The optimizer to apply to identifier generators whose optimizer is not configured explicitly. Only relevant for table- and sequence-based identifier generators. Other generators, such as UUID-based generators, will ignore this setting. The optimizer is responsible for pooling new identifier values, in order to reduce the frequency of database calls to retrieve those values and thereby improve performance. Environment variable: Show more |
|
|
||
型 |
デフォルト |
|||
The maximum size of the query plan cache. see # Environment variable: Show more |
int |
|
||
Default precedence of null values in Valid values are: Environment variable: Show more |
|
|
||
Enables IN clause parameter padding which improves statement caching. Environment variable: Show more |
boolean |
|
||
型 |
デフォルト |
|||
The time zone pushed to the JDBC driver. See Environment variable: Show more |
string |
|||
How many rows are fetched at a time by the JDBC driver. Environment variable: Show more |
int |
|||
The number of updates (inserts, updates and deletes) that are sent by the JDBC driver at one time for execution. Environment variable: Show more |
int |
|||
型 |
デフォルト |
|||
The size of the batches used when loading entities and collections.
Environment variable: Show more |
int |
|
||
The maximum depth of outer join fetch tree for single-ended associations (one-to-one, many-to-one). A Environment variable: Show more |
int |
|||
型 |
デフォルト |
|||
The maximum time before an object of the cache is considered expired. Environment variable: Show more |
||||
The maximum number of objects kept in memory in the cache. Environment variable: Show more |
長 |
|||
型 |
デフォルト |
|||
Existing applications rely (implicitly or explicitly) on Hibernate ignoring any DiscriminatorColumn declarations on joined inheritance hierarchies. This setting allows these applications to maintain the legacy behavior of DiscriminatorColumn annotations being ignored when paired with joined inheritance. Environment variable: Show more |
boolean |
|
||
型 |
デフォルト |
|||
Logs SQL bind parameters. Setting it to true is obviously not recommended in production. Environment variable: Show more |
boolean |
|
||
Show SQL logs and format them nicely. Setting it to true is obviously not recommended in production. Environment variable: Show more |
boolean |
|
||
Format the SQL logs if SQL log is enabled Environment variable: Show more |
boolean |
|
||
Highlight the SQL logs if SQL log is enabled Environment variable: Show more |
boolean |
|
||
Whether JDBC warnings should be collected and logged. Environment variable: Show more |
boolean |
|
||
If set, Hibernate will log queries that took more than specified number of milliseconds to execute. Environment variable: Show more |
長 |
|||
型 |
デフォルト |
|||
Select whether the database schema DDL files are generated or not. Accepted values: Environment variable: Show more |
string |
|
||
Filename or URL where the database create DDL file should be generated. Environment variable: Show more |
string |
|||
Filename or URL where the database drop DDL file should be generated. Environment variable: Show more |
string |
|||
型 |
デフォルト |
|||
The default flushing strategy, or when to flush entities to the database in a Hibernate session: before every query, on commit, … This default can be overridden on a per-session basis with See the javadoc of Environment variable: Show more |
|
|
期間フォーマットについて
To write duration values, use the standard 数字で始まる簡略化した書式を使うこともできます:
その他の場合は、簡略化されたフォーマットが解析のために
|
Do not mix クラスパスに無視したい
|
PostgreSQLサーバーをDockerで起動したいですか?
これは、永続化されない空のデータベースを起動します。簡単な実験に最適です! |
複数の永続化ユニット
複数の永続化ユニットの設定
Quarkusの設定プロパティを使用して複数の永続化ユニットを定義することができます。
quarkus.hibernate-orm.
名前空間のルートにあるプロパティで、デフォルトの永続化ユニットを定義します。例えば、次のスニペットではデフォルトのデータソースとデフォルトの永続化ユニットを定義しています:
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
マップをベースにした方法で名前付きの永続化ユニットを定義することができます:
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 | users という名前のデータソースを定義します。 |
2 | inventory という名前のデータソースを定義します。 |
3 | users という永続化ユニットを定義します。 |
4 | 永続化ユニットが使用するデータソースを定義します。 |
5 | この設定プロパティは重要ですが、説明は少し後になります。 |
6 | users という永続化ユニットを定義します。 |
デフォルトデータソースと名前付きデータソースを混在させることも、どちらか一方だけにすることもできます。 |
デフォルトの永続化ユニットは、デフォルトでデフォルトデータソースを使用します。名前付きの永続化ユニットの場合は 複数の永続化ユニットが同じデータソースを使用することもできます。 |
モデルクラスを永続化ユニットにアタッチする
モデルクラスを永続化ユニットにアタッチする方法は2つあり、混在できません:
-
packages
設定プロパティを使用します; -
@io.quarkus.hibernate.orm.PersistenceUnit
パッケージレベルのアノテーションを使用します。
両方が混在している場合はアノテーションが無視され、 packages
の設定プロパティのみが考慮されます。
packages
設定プロパティは簡単です:
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
この設定スニペットは2つの永続化ユニットを作成します:
-
デフォルトでは、
org.acme.model.defaultpu
パッケージのすべてのモデルクラスが含まれ、サブパッケージも含まれます。 -
users
という名前の永続化ユニットで、org.acme.model.user
パッケージのすべてのモデルクラスを含み、サブパッケージも含まれています。
複数のpackageを永続化ユニットにアタッチできます:
quarkus.hibernate-orm."users".packages=org.acme.model.shared,org.acme.model.user
org.acme.model.shared
と org.acme.model.user
パッケージの下にあるすべてのモデル・クラスは、 users
永続化ユニットにアタッチされます。
モデルクラスを複数の永続化ユニットにアタッチすることもサポートされます。
モデルクラスは与えられた永続化ユニットに一貫して追加される必要があります。つまり、与えられたエンティティのすべての依存するモデルクラス( |
Panacheエンティティは1つの永続化ユニットにのみアタッチできます。 複数の永続化ユニットに接続されたエンティティではPanacheを使用することはできません。しかし、この2つのアプローチを混在させることは可能で、Panacheエンティティと複数の永続化ユニットが必要な従来のエンティティを混在させることはできます。 もし、そのようなユースケースがあり、シンプルなPanacheのアプローチを乱すことなく実装する方法について素晴らしいアイデアがあれば、 quarkus-dev メーリングリストまでご連絡ください。 |
モデルクラスを永続化ユニットにアタッチする2つ目の方法は、パッケージレベルの @io.quarkus.hibernate.orm.PersistenceUnit
アノテーションを使用することです。繰り返しになりますが、この2つのアプローチを混在させることはできません。
上記のような構成を packages
の設定プロパティで取得するには、以下の内容の package-info.java
ファイルを作成します:
@PersistenceUnit("users") (1)
package org.acme.model.user;
import io.quarkus.hibernate.orm.PersistenceUnit;
1 | Jakarta Persistenceのアノテーションではなく、 @io.quarkus.hibernate.orm.PersistenceUnit アノテーションを使用することに注意してください。 |
モデルクラスの |
設定プロパティで行うのと同様で、アノテーションのつけられたパッケージだけでなく、そのすべてのサブパッケージも入れていることに注意してください。
CDI統合
QuarkusでHibernate ORMを使用することに慣れている方は、CDIを使用して EntityManager
をインジェクションしたことがあると思います:
@Inject
EntityManager entityManager;
これは、デフォルトの永続化ユニットの EntityManager
を注入します。
名前付き永続化ユニット ( この例では users
) の EntityManager
をインジェクトするのは簡単です:
@Inject
@PersistenceUnit("users") (1)
EntityManager entityManager;
1 | ここでも同じ @io.quarkus.hibernate.orm.PersistenceUnit アノテーションを使用しています。 |
全く同じ仕組みで名前付き永続化ユニットの EntityManagerFactory
をインジェクトすることができます:
@Inject
@PersistenceUnit("users")
EntityManagerFactory entityManagerFactory;
Activate/deactivate persistence units
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.
To deactivate a persistence unit at runtime, set quarkus.hibernate-orm[.optional name].active
to false
.
If a persistence unit is not active:
-
The
SessionFactory
will not start during application startup. -
Accessing the
EntityManagerFactory
/EntityManager
orSessionFactory
/Session
will cause an exception to be thrown.
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.
例えば、次のような設定です:
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=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.
カスタム設定プロファイル を使用すると、このような設定を簡素化できます。
以下のプロファイル固有の設定を上記の設定に追加することで、
|
With such a setup, you will need to take care to only ever access the active persistence unit.
To do so, you may define a CDI bean producer for the default Session
redirecting to the currently active named Session
, so that it can be injected directly, like this:
public class MyProducer {
@Inject
@DataSource("pg")
InjectableInstance<AgroalDataSource> pgDataSourceBean; (1)
@Inject
@DataSource("oracle")
InjectableInstance<AgroalDataSource> oracleDataSourceBean;
@Inject
@PersistenceUnit("pg")
Session pgSessionBean;
@Inject
@PersistenceUnit("oracle")
Session oracleSessionBean;
@Produces (2)
@ApplicationScoped
public Session session() {
if (pgDataSourceBean.getHandle().getBean().isActive()) { (3)
return pgSessionBean;
} else if (oracleDataSourceBean.getHandle().getBean().isActive()) { (3)
return oracleSessionBean;
} else {
throw new RuntimeException("No active datasource!");
}
}
}
@ApplicationScoped
public class MyConsumer {
@Inject
Session session; (4)
public void doSomething() {
// .. just use the injected session ...
}
}
1 | Don’t inject a DataSource or AgroalDatasource directly,
because that would lead to a failure on startup (can’t inject inactive beans).
Instead, inject InjectableInstance<DataSource> or InjectableInstance<AgroalDataSource> . |
2 | Declare a CDI producer method that will define the default session as either PostgreSQL or Oracle, depending on what is active. |
3 | Check whether datasource beans are active before retrieving the corresponding session. |
4 | This will get injected with the (only) active session. |
persistence.xml
を使用した場合のHibernate ORMのセットアップと設定
To set up and configure Hibernate ORM, using application.properties
is recommended,
but you can alternatively use a META-INF/persistence.xml
file.
This is mainly useful for migrating existing code to Quarkus.
Using a
クラスパスに無視したい
|
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.PostgreSQLDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<!--
Optimistically create the tables;
will cause background errors being logged if they already exist,
but is practical to retain existing data across runs (or create as needed) -->
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="jakarta.persistence.validation.mode" value="NONE"/>
</properties>
</persistence-unit>
</persistence>
persistence.xml
設定を使用する場合は、Hibernate ORM を直接設定することになるので、
この場合は hibernate.org のドキュメント を参照するのが適切です。
Quarkusの application.properties
で使用されているものと同じプロパティ名ではなく、同じデフォルト値が適用されるわけではありませんのでご注意ください。
XMLマッピング
QuarkusのHibernate ORMは、XMLマッピングをサポートしています。 orm.xml 形式(Jakarta Persistence) または hbm.xml 形式(Hibernate ORM専用、非推奨 )に従ってマッピングファイルを追加することができます:
-
in
application.properties
through the (build-time)quarkus.hibernate-orm.mapping-files
property. -
persistence.xml
の<mapping-file>
の要素を使用して。
XMLマッピングファイルは、ビルド時に解析されます。
そうしたくない場合は、 |
外部プロジェクトや jar でエンティティを定義する
QuarkusのHibernate ORMは、エンティティーに対するコンパイル時のバイトコード強化に依存しています。Quarkusアプリケーションを構築するのと同じプロジェクトでエンティティーを定義すれば、すべてがうまく動作します。
エンティティーが外部のプロジェクトやジャーから来ている場合は、空の META-INF/beans.xml
ファイルを追加することで、jarがQuarkusアプリケーションライブラリのように扱われるようにすることができます。
これにより、Quarkusは、エンティティが現在のプロジェクトの内部にあるかのようにインデックスを作成し、バイトコード強化をすることができます。
開発モードでのHibernate ORM
Quarkusの開発モードはフロントエンドやサービス、データベースアクセスが混在するアプリケーションにとても便利です。
それを生かすためにはいくつかの共通したアプローチがあります。
1つ目の選択肢は、 quarkus.hibernate-orm.database.generation=drop-and-create
と import.sql
を併用することです。
そうすることで、アプリケーション、特にエンティティに変更があるたびに、データベーススキーマが適切に再作成され、データフィクスチャ( import.sql
に保存)がゼロから再投入されます。これは環境を完全にコントロールするための最良の方法であり、Quarkusのライブリロードモードでは魔法のように機能します。エンティティの変更や import.sql
へのあらゆる変更が即座に反映され、アプリケーションを再起動しなくてもスキーマが更新されます!
|
2つ目の選択肢は quarkus.hibernate-orm.database.generation=update
を使用することです。 この方法は、多くのエンティティを変更するが本番データのコピーで作業する必要がある場合や、特定のデータベースのデータエントリーに基づくバグを再現する場合に最適です。 update
は Hibernate ORM によってベストエフォートで実行され、データ損失につながるデータベース構造の変更を含む特定の状況では失敗します。 例えば、外部キー制約に違反する構造を変更する場合、Hibernate ORM の挙動を助けてあげなければならないかもしれません。 しかし、開発中だとこれらの制限は許容範囲内です。
3つ目の選択肢は quarkus.hibernate-orm.database.generation=none
を使用することです。この方法は、本番データのコピーで作業しており、スキーマの変更を完全にコントロールしたい場合に最適です。あるいは、 Flyway や Liquibaseのようなデータベーススキーマ移行ツールを使用している場合です。
この方法では、エンティティに変更を加える時にデータベーススキーマに確実に適合させる必要があります。また、 validate
を使用して、Hibernateにスキーマが期待どおりかを確認させることもできます。
本番環境では、 quarkus.hibernate-orm.database.generation で drop-and-create と update は使用しないでください。
|
これらの方法は、Quarkusの設定プロファイルと組み合わせることで非常に強力になります。異なる 設定プロファイルを定義して、環境に応じて異なる動作を選択することができます。これは、現在必要としている開発スタイルに合わせて、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
カスタムプロファイルを使用して開発モードを開始することができます:
quarkus dev -Dquarkus.profile=dev-with-data
./mvnw quarkus:dev -Dquarkus.profile=dev-with-data
./gradlew --console=plain quarkusDev -Dquarkus.profile=dev-with-data
本番モードでのHibernate ORM
Quarkusにはデフォルトのプロファイルが付属しています ( dev
, test
と prod
)。また、様々な環境を記述するために独自のカスタムプロファイルを追加することができます ( staging
, prod-us
, など )。
Hibernate ORM Quarkusエクステンションでは、いくつかのデフォルト設定が、開発モードとテストモードで他の環境とは異なるように設定されています。
-
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
スキーマを管理するための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
エクステンションがインストールされている状態で Dev UI を開き、Flyway ペインの Datasources
リンクをクリックします。 Create Initial Migration
ボタンを押すと、次のようになります:
-
スキーマを生成するためにHibernateが実行するSQLを含んだ
db/migration/V1.0.0__{appname}.sql
ファイルが作成されます -
quarkus.flyway.baseline-on-migrate
が設定され、Flywayがベースラインとなるテーブルを自動的に作成するようになります -
quarkus.flyway.migrate-at-start
が設定され、アプリケーションの起動時にFlywayが自動的にマイグレーションを適用するようになります -
dev/test モードでリロードした後、DB をクリーンにするために
%dev.quarkus.flyway.clean-at-start
と`%test.quarkus.flyway.clean-at-start
が設定されます
このボタンはFlywayを素早く使い始めるためのものであり、本番環境でデータベーススキーマをどのように管理するかはユーザー次第です。特に migrate-at-start の設定はすべての環境に適しているとは限りません。
|
キャッシング
同じエンティティを頻繁に読み込むアプリケーションでは、Hibernate ORMのL2キャッシュを有効にするとパフォーマンスが向上します。
エンティティのキャッシュ
セカンドレベルキャッシュを有効にするには、キャッシュさせたいエンティティを @jakarta.persistence.Cacheable
でマークします:
@Entity
@Cacheable
public class Country {
int dialInCode;
// ...
}
エンティティが @Cacheable
でアノテーションされているときは、コレクションと他のエンティティとの関係を除いて、そのすべてのフィールド値がキャッシュされます。
これは、データベースに問い合わせることなくエンティティをロードできることを意味しますが、ロードされたエンティティがデータベースの最近の変更を反映していない可能性があることを意味するので注意が必要です。
コレクションとリレーションのキャッシング
コレクションとリレーションはキャッシュするために個別にアノテーションする必要があります。この場合、Hibernate固有の @org.hibernate.annotations.Cache
を使用する必要があり、さらに CacheConcurrencyStrategy
を指定する必要があります:
package org.acme;
@Entity
@Cacheable
public class Country {
// ...
@OneToMany
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
List<City> cities;
// ...
}
クエリのキャッシュ
クエリもL2キャッシュの恩恵を受けることができます。キャッシュされたクエリの結果は即座に呼び出し元に返すことができるので、データベース上でクエリを実行する必要がありません。
最近の変化を反映していない可能性があることを含意しているので注意が必要です。
クエリをキャッシュするには、 Query
インスタンス上でキャッシュ可能なものとしてマークします:
Query query = ...
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
NamedQuery
があれば、その定義で直接キャッシュを有効にすることができます。これは通常、エンティティ上で行われます:
@Entity
@NamedQuery(name = "Fruits.findAll",
query = "SELECT f FROM Fruit f ORDER BY f.name",
hints = @QueryHint(name = "org.hibernate.cacheable", value = "true") )
public class Fruit {
...
以上です。キャッシュ技術はすでにQuarkusに統合されてデフォルトで有効になってるのでキャッシュしても問題ないものを設定するだけで十分です。
キャッシュ領域の調整
キャッシュはデータの異なる部分を分離するために別々の領域にデータを保存します。このような領域には名前が付けられ、各領域を独立して設定したり、統計を監視したりするのに役立ちます。
デフォルトでは、エンティティは、その完全修飾名を冠した領域(例えば、 org.acme.Country
)にキャッシュされます。
org.acme.Country#cities
コレクションは保持するエンティティの完全修飾名とコレクションのフィールド名を #
文字で区切った名前の領域にキャッシュされます。
すべてのキャッシュされたクエリは、デフォルトでは、 default-query-results-region
と呼ばれる一つの専用の領域に保存されます。
すべてのリージョンは、デフォルトではサイズと時間で制限されています。デフォルトでは、最大で 10000
のエントリ数、最大で 100
秒のアイドル時間が設定されています。
各領域のサイズは、 quarkus.hibernate-orm.cache."<region_name>".memory.object-count
プロパティ( <region_name> を実際の領域名に置き換えてください)でカスタマイズできます。
最大アイドル時間を設定するには、 quarkus.hibernate-orm.cache."<region_name>".expiration.max-idle
プロパティ (<region_name> を実際のリージョン名に置き換えてください)で時間(下記の時間のフォーマットに関する注意を参照)を指定します。
領域名にドットが含まれている場合は二重引用符が必須です。次のようになります:
|
期間の値を書くには、標準の 数字で始まる簡略化した書式を使うこともできます:
その他の場合は、簡略化されたフォーマットが解析のために
|
キャッシュの制限
Quarkusで提供されているキャッシュ技術は現在のところ非常に初歩的で制約があります。
Quarkusの開発チームは最初から ある程度の キャッシュ機能があった方が何もないよりは良いと考えました。将来のリリースではより良いキャッシュソリューションが統合されることを期待しています。
これらのキャッシュはローカルに保持されているため、他のアプリケーションによって永続ストアに変更が加えられても無効化されたり更新されたりすることはありません。 また、同じアプリケーションの複数のコピーを(Kubernetes/OpenShiftなどのクラスタで)実行している場合、アプリケーションの別々のコピーのキャッシュは同期されません。 これらの理由から、ある種の仮定が成り立つ場合にのみキャッシュを有効にすることが適しています。私たちは、変化しないエンティティ、コレクション、およびクエリのみをキャッシュすることを強く推奨します。あるいは、そのようなエンティティが実際に変更され、古くなった(stale)ものを読み取ったとしても、アプリケーションの期待値に影響を与えないようにする必要があります。 このアドバイスに従うことで、アプリケーションがL2キャッシュから最高のパフォーマンスを引き出し、かつ予期せぬ動作を避けることができます。 不変のデータだけでなく、ある文脈では、可変のデータに対してもキャッシュを有効にすることが許容されるかもしれません。これは、頻繁に読み込まれ、ある程度の陳腐化を許容できるようなエンティティを選択した場合、必要なトレードオフとなり得ます。この「許容される陳腐化の度合い」は、eviction プロパティを設定することで調整できます。しかし、これは推奨されておらず、データに予期せぬ影響を与える可能性があるため、細心の注意を払って行う必要があります。 理想的には、変更可能なデータでキャッシュを有効にするのではなく、クラスタ化されたキャッシュを使用することがより良い解決策です。しかし、現時点では、Quarkusはそのような実装を提供していません:この必要性を知らせれば、チームがこれを考慮することができますので、お気軽にご連絡ください。 |
最後に、 hibernate.cache.use_second_level_cache
を false
に設定することで、L2キャッシュをグローバルで無効化できます。この設定は、 persistence.xml
設定ファイルで指定する必要があります。
L2キャッシュを無効にすると、すべてのキャッシュアノテーションは無視され、すべてのクエリはキャッシュを無視して実行されます。これは通常、問題を診断する場合にのみ有効です。
Hibernate Envers
Hibernate ORMのEnversエクステンションは、エンティティクラスのための簡単な監査/バージョン管理ソリューションを提供することを目的としています。
Quarkusでは、Enversには専用のQuarkus エクステンションがあります。 io.quarkus:quarkus-hibernate-envers
; これをプロジェクトに追加して使用を開始する必要があります。
<!-- Add the Hibernate Envers extension -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-envers</artifactId>
</dependency>
Quarkusの設定プロパティを使用して、複数の永続化ユニットを定義することができます。
Hibernate Enversの詳細については、 hibernate.org/orm/envers/を参照してください。
メトリクス
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.
制限事項など知っておくべきこと
Quarkusは使用するライブラリを変更しません。このルールはHibernate ORMにも適用されます。このエクステンションを使用すると、元のライブラリを使用した場合とほとんど同じエクスペリエンスが得られます。
しかし、両者は同じコードを共有していますが、Quarkusはいくつかのコンポーネントを自動的に設定し、いくつかの拡張ポイントにカスタム実装をインジェクションしています。
自動ビルド時間の強化
Hibernate ORMでは、ビルド時に拡張されたエンティティを使用できます。通常、これは必須ではありませんが便利でアプリケーションのパフォーマンスを向上させることができます。
通常は、ビルドスクリプトにHibernate Enhancementプラグインを含める必要がありますが、QuarkusではEnhancementステップがQuarkusアプリケーションのビルドと分析に統合されているため、その必要はありません。
Enhancement を使用しているため、エンティティで この制限は将来的に削除される可能性があります。 |
自動統合
- トランザクションマネージャーの統合
-
これを設定する必要はありません。Quarkusは自動的にNarayana Transaction Managerへの参照をインジェクションします。この依存関係は、Hibernate ORMエクステンションの推移的依存関係として自動的に含まれます。すべての設定はオプションです。詳細は、 Quarkusでのトランザクションの使用を参照してください。
- コネクションプール
-
どちらかを選択する必要はありません。上記の例のようにデータソースを設定するだけで、Hibernate ORMがAgroalを使用するように設定されます。このコネクションプールの詳細については、 Quarkus - データソースを参照してください。
- L2キャッシュ
-
As explained earlier in the Caching section, 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.
制約事項
- クラスパスに重複したファイルがある場合のXMLマッピング
-
XML マッピング ファイルは一意のパスを持つことが期待されます。
実際には、クラスパスに XML マッピングファイルを重複して配置するのは、非常に特殊なシナリオの場合のみです。たとえば、2つのJARに(まったく同じパスで異なるJARにある)
META-INF/orm.xml
ファイルが含まれている場合、マッピングファイルのパスMETA-INF/orm.xml
は、 <code>META-INF/orm.xml</code> ファイルと同じJAR のpersistence.xml
からしか参照できません。 - JMX
-
管理 Bean は GraalVM ネイティブイメージでは動作しません。したがって、ネイティブイメージにコンパイルすると、JMX Bean に統計と管理操作を登録する Hibernate の機能は無効になります。ネイティブ・イメージがJMXのサポートを実装することは目標ではないので、この制限は永久に続くと思われます。このようなメトリクスはすべて、他の方法でアクセスすることができます。
- JACCの統合
-
GraalVMのネイティブイメージを構築する際には、JACCと統合するHibernate ORMの機能は無効になります。なぜなら、JACCはネイティブ・モードでは利用できず、有用でもないからです。
- セッションをThreadLocalコンテキストにバインドする
-
Hibernate ORMの
ThreadLocalSessionContext
ヘルパーはサポートが実装されていないため、使用できません。QuarkusはCDIサポートを最初から提供しているので、インジェクションやプログラムによるCDIルックアップがより良いアプローチとなります。この機能は、リアクティブコンポーネントやより現代的なコンテキスト伝搬技術ともうまく統合できないため、このレガシーな機能には未来がないと考えました。ThreadLocalにバインドする必要がある場合は、容易に独自のコードで実装出来る筈です。 - JNDI
-
JNDI技術は、異なるコンポーネントを統合するために他のランタイムで一般的に使用されています。一般的な使用例は、Java EnterpriseサーバーでTransactionManagerとDatasourceコンポーネントを名前にバインドし、Hibernate ORMがこれらのコンポーネントを名前で検索するように設定することです。しかし、Quarkusでは、コンポーネントが直接注入されるため、このユースケースは適用されず、JNDIサポートは不要なレガシーとなります。JNDIの予期せぬ使用を避けるため、QuarkusのHibernate ORMエクステンションでは、JNDIの完全なサポートは無効になっています。これは、セキュリティ上の予防策であり、最適化でもあります。
その他の特記すべき相違点
import.sql
のフォーマット-
データベースをセットアップするために
import.sql
をインポートする際、QuarkusはHibernate ORMを再構成し、各ステートメントの最後にセミコロン( ';' )を必要とすることに留意してください。Hibernateのデフォルトでは、改行以外の終端文字を必要とせず、1行に1つのステートメントがあります。既存のスクリプトを再利用する場合は、終端文字として「;」を使用するようにスクリプトを変換することを忘れないでください。これは、複数行のステートメントを可能にし、人間が使いやすいフォーマットにするために役立ちます。
シンプルになったHibernate ORM with Panache
Hibernate ORM with Panache エクステンションはアクティブレコードスタイルのエンティティ(およびリポジトリ)を提供してHibernate ORMを簡単に使えるようにし、Quarkusでエンティティを簡単に楽しく書けるようにすることに重点を置いています。
データソースの設定
データソースの設定は非常にシンプルですが、技術的にはQuarkus用のAgroal接続プールエクステンションによって実装されているため、別のガイドで説明します。
詳細は Quarkus - データソースをご覧ください。
マルチテナンシー
「マルチテナントという用語は、一般的にソフトウェア開発に適用され、アプリケーションの単一の実行インスタンスが同時に複数のクライアント(テナント)にサービスを提供するアーキテクチャを示す。これはSaaSソリューションでは非常に一般的です。このようなシステムでは、さまざまなテナントに関連する情報(データ、カスタマイズなど)を分離することが特に課題となります。これには、データベースに格納された各テナントが所有するデータも含まれます」( Hibernateユーザーガイド )。
Quarkusは現在、 分離データベース アプローチ、 分離スキーマ アプローチ、 discriminator アプローチをサポートしています。
マルチテナンシーの動作を確認するには、 hibernate-orm-multi-tenancy-quickstart クイックスタートをご覧ください。
アプリケーションの記述
まず、 /{tenant}
のエンドポイントを実装することから始めましょう。以下のソースコードからわかるように、これは通常の Jakarta REST リソースです:
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@ApplicationScoped
@Path("/{tenant}")
public class FruitResource {
@Inject
EntityManager entityManager;
@GET
@Path("fruits")
public Fruit[] getFruits() {
return entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
.getResultList().toArray(new Fruit[0]);
}
}
受信したリクエストからテナントを解決し、特定のテナント構成にマッピングするためには、 io.quarkus.hibernate.orm.runtime.tenant.TenantResolver
インターフェースの実装を作成する必要があります。
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
import io.vertx.ext.web.RoutingContext;
@PersistenceUnitExtension (1)
@RequestScoped (2)
public class CustomTenantResolver implements TenantResolver {
@Inject
RoutingContext context;
@Override
public String getDefaultTenantId() {
return "base";
}
@Override
public String resolveTenantId() {
String path = context.request().path();
String[] parts = path.split("/");
if (parts.length == 0) {
// resolve to default tenant config
return getDefaultTenantId();
}
return parts[1];
}
}
1 | TenantResolverの実装に @PersistenceUnitExtension という修飾語を付けて、Quarkusにデフォルトの永続化ユニットで使用することを伝えます。
名前付きの永続化ユニット には、 |
2 | Beanは、テナントの解決が入ってくるリクエストに依存するため @RequestScoped にします。 |
上記の実装ではテナントはリクエストパスから解決されるので、テナントが推測できない場合はデフォルトのテナント識別子が返されます。
OIDCマルチテナンシーも使用していて、OIDCとHibernate ORMの両方のテナントIDが同じで、Vert.xの
|
アプリケーションの設定
一般的に、Hibernate ORMのデータベース生成機能をマルチテナンシーのセットアップと組み合わせて使用することはできません。そのため、この機能を無効にして、テーブルがスキーマごとに作成されるようにする必要があります。以下のセットアップでは、 Flyway エクステンションを使用してこの目的を達成します。
SCHEMAアプローチ
The same data source will be used for all tenants and a schema has to be created for every tenant inside that data source.
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.
We use the default datasource here, but could use a named datasource if we wanted to, by following instructions there. |
3 | Configure the datasource. |
4 | 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
) の例を示します。
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');
データベースアプローチ
すべてのテナントに対して、 TenantResolver
が返すのと同じ識別子を持つ名前付きデータソースを作成する必要があります。
With this approach, all datasources used by the same persistence unit
are assumed to point to a database of the same vendor (same 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.
This is only to allow Quarkus to determine the Hibernate ORM dialect to use; see this section for details. |
4 | Configure the datasource for one tenant, base . |
5 | Configure Flyway for database initialization for tenant base ,
because schema generation by Hibernate ORM is not supported in this case. |
6 | Configure the datasource for another tenant.
There could be more tenants, but here we’re stopping at two. |
7 | Configure Flyway for database initialization for tenant mycompany ,
because schema generation by Hibernate ORM is not supported in this case. |
以下は、設定されたフォルダー src/main/resources/database
に作成する Flyway 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');
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 アプローチ
デフォルトのデータソースがすべてのテナントに使用されます。 @TenantId
でアノテーションされたフィールドを定義しているすべてのエンティティには、そのフィールドが自動的に入力され、クエリで自動的にフィルタリングされます。
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. |
テナント接続をプログラムで解決
サポートするさまざまなテナントに対してより動的な設定が必要で、設定ファイルに複数のエントリを残したくない場合は、 io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver
インターフェースを使用して接続を取得するための独自のロジックを実装することができます。このインターフェースを実装するアプリケーションスコープのBeanを作成し、 @PersistenceUnitExtension
(または 名前付き永続化ユニットの場合は @PersistenceUnitExtension("nameOfYourPU")
)アノテーションを付けることで、現在のQuarkusのデフォルトの実装である io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver
を置き換えることができます。カスタムコネクションリゾルバを使用すると、例えばデータベースからテナント情報を読みとった情報に基づいて実行時にテナントごとに接続を作成することができます。
インターセプター
適切な修飾子を持つ CDI Bean を定義するだけで、SessionFactory
に org.hibernate.Interceptor
を割り当てることができます。
@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 | インターセプターの実装に @PersistenceUnitExtension の修飾子を付けて、Quarkusにデフォルトの永続化ユニットで使用されるように伝えます。
名前付きの永続化ユニット には |
2 | org.hibernate.EmptyInterceptor を拡張するか、 org.hibernate.Interceptor を直接実装します。 |
3 | 必要に応じてメソッドを実装します。 |
デフォルトでは、 エンティティマネージャーごとに1つのインターセプターのインスタンスを作成するには、Beanに |
Hibernate ORM 自体の制限により、 |
ステートメントインスペクター
適切な修飾子を持つCDI Beanを定義するだけで、SessionFactory
に org.hibernate.engine.jdbc.spi.StatementInspector
を割り当てることができます:
@PersistenceUnitExtension (1)
public class MyStatementInspector implements StatementInspector { (2)
@Override
public String inspect(String sql) {
// ...
return sql;
}
}
1 | ステートメントインスペクターの実装に @PersistenceUnitExtension という修飾子を付けて、Quarkus にデフォルトの永続化ユニットで使用するように指示します。
名前付きの永続化ユニット には |
2 | org.hibernate.engine.jdbc.spi.StatementInspector を実装してください。 |
Customizing JSON/XML serialization/deserialization
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.
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:
import io.quarkus.hibernate.orm.JsonFormat;
import org.hibernate.type.format.FormatMapper;
@JsonFormat (1)
@PersistenceUnitExtension (2)
public class MyJsonFormatMapper implements FormatMapper { (3)
@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
// ...
}
@Override
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
// ...
}
}
1 | 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.
名前付きの永続化ユニット には |
||
3 | Implement org.hibernate.type.format.FormatMapper . |
In case of a custom XML format mapper, a different CDI qualifier must be applied:
import io.quarkus.hibernate.orm.XmlFormat;
import org.hibernate.type.format.FormatMapper;
@XmlFormat (1)
@PersistenceUnitExtension (2)
public class MyJsonFormatMapper implements FormatMapper { (3)
@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
// ...
}
@Override
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
// ...
}
}
1 | 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.
名前付きの永続化ユニット には |
3 | Implement org.hibernate.type.format.FormatMapper . |
Format mappers must have both Having multiple JSON (or XML) format mappers registered for the same persistence unit will result in an exception, because of the ambiguity. |