Simplified Hibernate with Quarkus Data Hibernate
experimentalQuarkus Data Hibernate is the perfect solution if you want to get started using Hibernate in Quarkus.
|
この技術は、experimentalと考えられています。 experimental モードでは、アイデアを成熟させるために早期のフィードバックが求められます。ソリューションが成熟するまでの間、プラットフォームの安定性や長期的な存在を保証するものではありません。フィードバックは メーリングリスト や GitHubの課題管理 で受け付けています。 とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。 |
| このエクステンションは現在実験段階にあるため、API が変更される可能性があります。特に、クラス名やパッケージ名、さらにはモジュール名も変更される可能性があります。現在、パブリックプレビュー化を進めており、皆様からのフィードバックを求めています。フィードバックは Zulip または GitHub issues までお寄せください。 |
ウォークスルー
Let’s take a progressive approach to learning how to use Quarkus Data Hibernate, and start with a simple entity.
このガイドでは、 Hibernate またはその基盤となる Jakarta Persistence 仕様 のどちらかの具体的な使用法については詳しく説明しません。なぜなら、どちらもすでに優れたドキュメントがあり、より深い知識を得るために活用できるからです。
エクステンションのインポートと設定
<!-- Import the Quarkus Data Hibernate extension -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-data-hibernate</artifactId>
</dependency>
<!-- Pick your database driver -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
// Import the Quarkus Data Hibernate extension
implementation("io.quarkus:quarkus-data-hibernate")
// Pick your database driver
implementation("io.quarkus:quarkus-jdbc-postgresql")
また、必須の Hibernate プロセッサーを設定する必要があります。
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- This setting is required for the annotation processor dependencies to be managed by Quarkus.
More information is available in Maven compiler plugin documentation:
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#annotationProcessorPathsUseDepMgmt -->
<annotationProcessorPathsUseDepMgmt>true</annotationProcessorPathsUseDepMgmt>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-processor</artifactId>
<!-- Note, no artifact version is required, it's managed by Quarkus. -->
</path>
<!-- other processors that may be required by your app -->
</annotationProcessorPaths>
<!-- Other compiler plugin configuration options -->
</configuration>
</plugin>
// Enforce the version management of your annotation processor dependencies,
// so that there's no need to define an explicit version of the hibernate-processor
annotationProcessor enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
annotationProcessor 'org.hibernate.orm:hibernate-processor'
| サポートされている JDBC ドライバー のいずれかを使用できます。 |
Add the relevant configuration properties in application.properties.
application.propertiesquarkus.datasource.db-kind = postgresql (1)
%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db
%prod.quarkus.hibernate-orm.schema-management.strategy=create (2)
| 1 | Configure the datasource for production, relying on Dev Services for connection information in tests / dev mode. |
| 2 | Configure Hibernate ORM to create the schema on startup in production, which is useful for experimentation, but rely on convenient defaults in tests / dev mode. |
最初のエンティティ
What this guide will focus on is the basics of how to use Quarkus Data Hibernate, so let’s start with how to create an entity:
import io.quarkus.hibernate.panache.PanacheEntity;
import jakarta.persistence.Entity;
import java.time.LocalDate;
@Entity
public class Cat extends PanacheEntity {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
}
Java クラスをデータベーステーブルにマッピングするには、次の手順を実行します。
-
クラスを作成する
-
@Entityでアノテーションを付けます。 -
PanacheEntityを継承させる -
フィールドを
publicにする
これで完了です。エンティティのインスタンス作成、データベースへの永続化を開始できます。その後は、フィールドへのすべての変更が自動的にデータベースに送信され (明示的な update 命令は不要)、データベースから削除することもできます。
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.util.List;
public class Code {
@Transactional (1)
public void method() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
cat.persist();
// Make a change, no need to update it
cat.name = "Luckynou";
// Delete our cat
cat.delete();
}
}
| 1 | これは、トランザクション内で操作を実行するために、データベースと対話するすべてのメソッドで必要です。 |
最初のリポジトリ
エンティティの作成、更新、削除の方法がわかったので、データベースをクエリしてエンティティを検索する方法や、削除クエリを実行する方法を見てみましょう。
クエリ操作はエンティティのインスタンスに属さないため、これらの操作は Repository と呼ばれる別の型に配置します。また、それらのクエリは操作対象のエンティティと密接に結びついているため、エンティティ内にネストされたインターフェースに配置することをお勧めします。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import jakarta.persistence.Entity;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat extends PanacheEntity {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository<Cat> {
@Find
Cat findByName(String name);
@HQL("where breed = CUTE")
List<Cat> findCute();
@HQL("delete from Cat where name = :name")
long deleteByName(String name);
@HQL("delete from Cat where breed = HAIRLESS")
long deleteHairless();
}
}
A Quarkus Data Hibernate repository is:
-
エンティティ内にネストされたインターフェース (ただし、好みに応じてトップレベルインターフェースでも構いません)
-
PanacheRepositoryインターフェースを拡張し、問題のエンティティを型パラメーターとして持つ -
そして、
@Findまたは@HQL、あるいは@SQL(ネイティブクエリ用) のいずれかでアノテーションが付けられたクエリメソッド、またはクエリの実装を含むdefaultメソッドが含まれる
@Find メソッドを使用して、メソッドのパラメーターでクエリを構築することにより、単一のインスタンスまたはエンティティのコレクションを検索できます。Hibernate ドキュメントには、 これに関する必要なすべての情報 があります。
@Find
public List<Cat> findAllCats(); (1)
@Find
public long countAllCats(); (2)
@Find
public Cat findCatByNameAndBreed(String name, Breed breed); (3)
| 1 | ファインダーメソッドはエンティティのコレクションを返す場合があります |
| 2 | または COUNT クエリを生成する |
| 3 | または単一のエンティティ、およびクエリ対象のエンティティすべてに一致する必要がある任意の数のパラメーター |
あるいは、HQL および SQL クエリをサポートする @HQL または @SQL クエリを使用できます。繰り返しになりますが、Hibernate ドキュメントには 必要なすべての情報 があります。
@HQL("from Cat")
public List<Cat> findAllCats(); (1)
@HQL("select min(birth) from Cat")
public LocalDate oldestCat(); (2)
@HQL("where name = :name and breed = :breed")
public Cat findCatByNameAndBreed(String name, Breed breed); (3)
| 1 | クエリメソッドはエンティティのコレクションを返す場合があります |
| 2 | または単一カラムのプロジェクション、さらに Object[] および List<Object[]> 型を使用した複数カラムのプロジェクションも可能 |
| 3 | クエリはもちろんメソッドのパラメーターを参照できます。 |
生成されたファインダーメソッドとクエリメソッドの利点は、ビルド時 に型チェックされることです。これにより、エンティティ名、そのフィールド、HQL/SQL 構文、またはパラメーター名に誤植がないことが検証され、保証されます。
リポジトリの使用
リポジトリを使用するには、使用したい場所にインジェクトするだけです。
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.util.List;
public class Code {
@Inject
Cat.Repo repo;
@Transactional
public void method() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
cat.persist();
// Make a change, no need to update it
cat.name = "Luckynou";
// Find our cat
cat = repo.findByName("Luckynou");
// Find cute cats
List<Cat> cuteCats = repo.findCute();
// Delete our cat
cat.delete();
// Delete queries
repo.deleteByName("Lucky");
repo.deleteHairless();
}
}
さらに、生成されたエンティティの 生成されたメタモデル にある便利なショートカット生成静的メソッドを使用してリポジトリにアクセスすることもできます。これは、操作の発見に非常に役立ち (単に Cat_.repo(). と入力してすべてのメソッドを確認する)、メソッドの外部でインジェクトされたフィールドを追加する回り道を避けるためにも便利です。この起動コードのように、すべての猫を削除する例があります (本番環境では行わないでください!!):
import io.quarkus.runtime.Startup;
import jakarta.transaction.Transactional;
public class OnStart {
@Startup
@Transactional
public void startupMethod() {
Cat_.repo().deleteAll();
}
}
エンティティのネストされたインターフェースとして定義するすべてのリポジトリは、生成されたメタモデルクラスの、リポジトリと同じ名前のアクセサーメソッドの下で利用できます。これはリポジトリ型をインジェクトすることと厳密に同等であり、実際には内部で CDI を使用してリポジトリを検索します。
PanacheRepository スーパークラス
Hibernate ORM と Hibernate Reactive with Panache の以前のバージョンと同様に、PanacheRepository 型には、エンティティを操作するために必要なほとんどの操作がすぐに利用できるようになっています。例えば次のとおりです。
import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.hibernate.Session;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
public class Code {
@Inject
Cat.Repo repo;
@Transactional
public void repositoryOperations(Cat cat) {
// entity operations
repo.persist(cat);
repo.delete(cat);
boolean isPersistent = repo.isPersistent(cat);
// operations on all entities
long count = repo.count();
long deleted = repo.deleteAll();
List<Cat> allCats = repo.listAll();
repo.streamAll().forEach(kitty -> kitty.name = kitty.name.toUpperCase());
PanacheBlockingQuery<Cat> catQuery = repo.findAll();
// operations on the Hibernate session
repo.flush();
Session session = repo.getSession();
// ID-related operations
boolean wasDeleted = repo.deleteById(cat.id);
Cat foundCat = repo.findById(cat.id);
Optional<Cat> optionalCat = repo.findByIdOptional(cat.id);
}
}
タイプセーフでないクエリ
これまでに示したように、生成されたファインダーメソッドとクエリメソッドを使用すると、すべてがビルド時 に検証されますが、タイプセーフでないクエリを作成したい場合は、PanacheRepository が提供するメソッドをいつでも使用できます。
import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.hibernate.Session;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
public class Code {
@Inject
Cat.Repo repo;
@Transactional
public void nonTypeSafeQueries() {
// Find a cat by name
Cat cat = repo.find(Cat_.NAME, "Lucky").singleResult();
// All cute cats
List<Cat> cuteCats = repo.list(Cat_.BREED, Cat.Breed.CUTE);
// Cats with no known birth date
repo.stream(Cat_.BIRTH+" is null").forEach(kitty -> System.err.println(kitty));
// Get rid of non-cute cats
long deleted = repo.delete(Cat_.BREED, Cat.Breed.HAIRLESS);
// Count ugly cats with no name
repo.count("breed = ?1 and name is null", Cat.Breed.HAIRLESS);
// Make every cat cute
repo.update("breed = CUTE");
}
}
エンティティ識別子について
上記の例では、PanacheEntity 型を拡張し、データベース識別子を定義しませんでした。これは、PanacheEntity がデフォルトで生成されるデータベース識別子を備えているため、それについて心配する必要がないためです。これは、Long 型の生成されたデータベース識別子を提供する WithId.AutoLong クラスを拡張することで実現されます。
String 識別子には WithId.AutoString を、UUID 識別子には WithId.AutoUUID を拡張することもできますし、WithId<IdType> を拡張して次の形式の属性を自動的に取得することもできます。
@Id
public IdType id;
| The identifier will not be automatically generated in this case, so you will have to set its value manually on creation. |
もちろん、独自のデータベース識別子を明示的に指定し、エンティティで PanacheEntity.Managed インターフェースを実装し、リポジトリには PanacheRepository.Managed インターフェースを使用して、データベース識別子型を指定することもできます。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class CatWithId implements PanacheEntity.Managed {
@Id
public String id;
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository.Managed<CatWithId, String> {
@Find
CatWithId findByName(String name);
@HQL("where breed = CUTE")
List<CatWithId> findCute();
@HQL("delete from Cat where name = :name")
long deleteByName(String name);
@HQL("delete from Cat where breed = HAIRLESS")
long deleteHairless();
}
}
使用できるエンティティのスーパークラスとその提供する ID 型のリストを次に示しますが、独自の ID を定義する場合はこれらの型のいずれかを拡張する必要がないことに注意してください。
| ID 型 | スーパータイプ | ショートカットタイプ |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
ステートレスセッションの使用
デフォルトでは、PanacheEntity のサブタイプは Hibernate ORM によって管理され、エンティティーに対するすべての変更は、明示的な update 操作を必要とせずに自動的にデータベースに送信されます。
一方で、すべての update 操作を明示的にしたい場合は、Hibernate ORM が ステートレスセッション と呼ぶものを使用する必要があります。
この場合、WithId.AutoLong (またはその他の ID クラス、あるいは独自の ID) を拡張し、PanacheEntity.Stateless および PanacheRepository.Stateless インターフェースを実装する必要があります。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import io.quarkus.hibernate.panache.WithId;
import jakarta.persistence.Entity;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat extends WithId.AutoLong implements PanacheEntity.Stateless {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository.Stateless<Cat, Long> {
@Find
Cat findByName(String name);
@HQL("where breed = CUTE")
List<Cat> findCute();
@HQL("delete from Cat where name = :name")
long deleteByName(String name);
@HQL("delete from Cat where breed = HAIRLESS")
long deleteHairless();
}
}
ご覧のとおり、エンティティー定義は2つのインターフェースを除けばまったく同じです。しかし、これでエンティティーは 管理 されなくなるため、すべての更新操作を明示的に行う必要があります。
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.hibernate.StatelessSession;
import java.time.LocalDate;
import java.util.List;
public class Code {
@Inject
Cat.Repo repo;
@Transactional
public void method() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
cat.insert();
// Make a change, we need to update it
cat.name = "Luckynou";
cat.update();
// Find our cat
cat = repo.findByName("Luckynou");
// Find cute cats
List<Cat> cuteCats = repo.findCute();
// Delete our cat
cat.delete();
// Delete queries
repo.deleteByName("Lucky");
repo.deleteHairless();
}
}
ご覧のとおり、管理対象エンティティーとの唯一の違いは次のとおりです。
-
エンティティーインスタンスのフィールドに対する変更は、エンティティーまたはそのリポジトリーのいずれかで
update()を呼び出すことで、明示的にデータベースにプッシュする必要があります。 -
データベースにエンティティーを挿入するには、
persist()の代わりにinsert()を呼び出す必要があります。 -
セッションの型は
SessionではなくStatelessSessionになります。
しかし、ほとんどそれだけです。特にクエリーやエンティティーの取得方法については、それ以外のすべては同じままです。
| ステートレスセッションを使用している場合、Jakarta Data 型のリポジトリーも使用できます。 |
リアクティブにしてみよう
リアクティブアプリケーションでエンティティーを使い始めたいですか?まずは pom.xml に Hibernate Reactive とデータベースのデータソースをインポートすることから始めましょう。
<!-- Enable Hibernate Reactive support -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive</artifactId>
</dependency>
<!-- Pick your database reactive driver -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<!-- FIXME: this will not be required in the future -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache-common</artifactId>
</dependency>
// Enable Hibernate Reactive support
implementation("io.quarkus:quarkus-hibernate-reactive")
// Pick your database reactive driver
implementation("io.quarkus:quarkus-reactive-pg-client")
// FIXME: this will not be required in the future
implementation("io.quarkus:quarkus-hibernate-reactive-panache-common")
| サポートされているリアクティブドライバー のいずれかを使用できます。 |
application.properties でリアクティブデータソースを設定してください (ただし、開発モードでは必須ではありません。設定しない場合、dev services が提供されます)。
quarkus.datasource.username = quarkus_test
quarkus.datasource.password = quarkus_test
quarkus.datasource.reactive.url = vertx-reactive:postgresql://localhost/quarkus_test (1)
さて、コードでは、通常の管理対象セッションエンティティーとの唯一の違いは次のとおりです。
-
エンティティーが
PanacheEntity.Reactiveを実装します。 -
リポジトリーが
PanacheRepository.Reactiveを拡張します。 -
すべての操作は、
Tの代わりにUni<T>を返します。これは標準的な Mutiny リアクティブ型です。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import io.quarkus.hibernate.panache.WithId;
import io.smallrye.mutiny.Uni;
import jakarta.persistence.Entity;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat extends WithId.AutoLong implements PanacheEntity.Reactive {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository.Reactive<Cat, Long> {
@Find
Uni<Cat> findByName(String name);
@HQL("where breed = CUTE")
Uni<List<Cat>> findCute();
@HQL("delete from Cat where name = :name")
Uni<Integer> deleteByName(String name);
@HQL("delete from Cat where breed = HAIRLESS")
Uni<Integer> deleteHairless();
}
}
そして、エンティティーまたはそのリポジトリーを使用したい場合、@Transactional の代わりに @WithTransaction を使用する以外は、Mutiny を使用するリアクティブコードで通常行うように操作を構成する必要がありますが、それ以外は操作はまったく同じです。
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
public class Code {
@Inject
Cat.Repo repo;
@WithTransaction
public Uni<Void> method() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
return cat.persist()
// Make a change, no need to update it
.invoke(() -> cat.name = "Luckynou")
// Find our cat
.chain(() -> repo.findByName("Luckynou"))
// Find cute cats
.chain((Cat foundCat) -> repo.findCute())
// Delete our cat
.chain((List<Cat> cuteCats) -> cat.delete())
// Delete queries
.chain(v -> repo.deleteByName("Lucky"))
.chain((Integer deletedCount) -> repo.deleteHairless())
.replaceWithVoid();
}
}
リアクティブかつステートレス
エンティティーの変更を手動で管理したい場合は、エンティティーに PanacheEntity.Reactive.Stateless を、リポジトリーに PanacheRepository.Reactive.Stateless を使用することで、ステートレスセッションを使用できます。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import io.quarkus.hibernate.panache.WithId;
import io.smallrye.mutiny.Uni;
import jakarta.persistence.Entity;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat extends WithId.AutoLong implements PanacheEntity.Reactive.Stateless {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository.Reactive.Stateless<Cat, Long> {
@Find
Uni<Cat> findByName(String name);
@HQL("where breed = CUTE")
Uni<List<Cat>> findCute();
@HQL("delete from Cat where name = :name")
Uni<Integer> deleteByName(String name);
@HQL("delete from Cat where breed = HAIRLESS")
Uni<Integer> deleteHairless();
}
}
そして、使用するコードについて言えば、唯一の変更点は、update() を手動で呼び出す標準的な使用方法と、persist() の代わりに insert() を使用することです。
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import java.time.LocalDate;
import java.util.List;
public class Code {
@Inject
Cat.Repo repo;
@WithTransaction
public Uni<Void> method() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
return cat.insert()
// Make a change, we need to update it
.chain(() -> {
cat.name = "Luckynou";
return cat.update();
})
// Find our cat
.chain(() -> repo.findByName("Luckynou"))
// Find cute cats
.chain((Cat foundCat) -> repo.findCute())
// Delete our cat
.chain((List<Cat> cuteCats) -> cat.delete())
// Delete queries
.chain(v -> repo.deleteByName("Lucky"))
.chain((Integer deletedCount) -> repo.deleteHairless())
.replaceWithVoid();
}
}
| ステートレスセッションを使用している場合、Jakarta Data 型のリポジトリーも使用できます。 |
ブロッキング、リアクティブ、管理、ステートレスなコードの組み合わせ
エンティティーをブロッキング管理セッションコードと、リアクティブステートレスコードの両方で使用したいとしましょう。これは、エンティティーのどのスーパータイプを選択しても可能です。ほとんどのユースケースを表すスーパータイプを選択すべきですが、どれを選んだとしても、エンティティーで .statelessReactive() メソッドを使用することで、常に代替操作を取得できます。
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
public class Code {
@Transactional
public void blockingManagedMethod() {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Persist the cat
cat.persist();
// Make a change, no need to update it
cat.name = "Luckynou";
}
@WithTransaction
public Uni<Void> reactiveStatelessMethod(Long catId) {
Cat cat = new Cat();
cat.name = "Lucky";
cat.birth = LocalDate.of(2015, 01, 12);
cat.breed = Cat.Breed.CUTE;
// Insert the cat
return cat.statelessReactive().insert()
.chain(() -> {
// Make a change, we need to update it
cat.name = "Luckynou";
return cat.statelessReactive().update();
});
}
}
代替のエンティティー操作はすべて、これらのメソッドから利用できます。
| セッション型 | エンティティーアクセサー | 同等のエンティティー型 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
同様に、すべてのリポジトリ操作に対して、生成されたメタモデルアクセサーからエンティティの代替リポジトリを取得したり、@Inject でそれらを注入したりできます。
| セッション型 | メタモデルアクセサー | リポジトリの型 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
しかし、エンティティ内でカスタム操作のための任意の数のリポジトリを定義することもできます。
import io.quarkus.hibernate.panache.PanacheEntity;
import io.quarkus.hibernate.panache.PanacheRepository;
import io.smallrye.mutiny.Uni;
import jakarta.persistence.Entity;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat extends PanacheEntity {
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository<Cat> {
@Find
Cat findByName(String name);
}
public interface TheOtherRepo extends PanacheRepository.Reactive.Stateless<Cat, Long> {
@Find
Uni<Cat> findByName(String name);
}
}
そして、両方のタイプのリポジトリを注入することができます。
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
public class Code {
@Inject
Cat.Repo repo;
@Inject
Cat.TheOtherRepo theOtherRepo;
@Transactional
public void blockingManagedMethod() {
Cat cat = repo.findByName("Lucky");
}
@WithTransaction
public Uni<Void> reactiveStatelessMethod() {
return theOtherRepo.findByName("Lucky")
.invoke(cat -> System.err.println(cat))
.replaceWithVoid();
}
}
これらのリポジトリを注入する代わりに、Cat_.repo() と Cat_.theOtherRepo() を使用して、生成されたメタモデルアクセサーを使用することももちろん可能です。
|
要約
ここに、操作モデルの好みと必要な ID の型に応じて、エンティティおよびリポジトリのスーパータイプとして利用できるすべてのオプションをリストした表があります。独自の ID エンティティフィールドを定義する場合、WithId<Id> を拡張する必要がないことを覚えておいてください。
| セッション型 | ID 型 | エンティティの親クラス | エンティティの親インターフェース | リポジトリのスーパータイプ |
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
起動コード
ブロッキング操作の場合、起動コードの呼び出しは非常に簡単です。
import io.quarkus.runtime.Startup;
import jakarta.transaction.Transactional;
public class OnStart {
@Startup
@Transactional
public void startupMethod() {
Cat_.repo().deleteAll();
}
}
リアクティブ操作の場合も同様です。
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.quarkus.runtime.Startup;
import io.smallrye.mutiny.Uni;
public class OnStart {
@Startup
@WithTransaction
Uni<Void> startupMethod(){
return Cat_.repo().deleteAll().replaceWithVoid();
}
}
Jakarta Data
当然ながら、Jakarta Data を使用してリポジトリを定義することもできます。そのためには、以下のモジュールをインポートする必要があります。
<dependency>
<groupId>jakarta.data</groupId>
<artifactId>jakarta.data-api</artifactId>
</dependency>
implementation 'jakarta.data:jakarta.data-api'
その後、前に説明したように、PanacheEntity のバリアントを拡張するかどうかにかかわらず、必要に応じてエンティティを記述できます。
リポジトリについては、Jakarta Data 1.0 がステートレスなバリアントのみをサポートしているため(マネージドエンティティは次期 1.1 バージョンに追加される予定です)、ステートレスまたはマネージドエンティティをサポートするために PanacheRepository バリアントを使用することをお勧めします。ただし、ブロッキングおよびリアクティブの両方のバリアントがサポートされています。
そうでない場合は、@HQL を @Query に置き換え、@Find のパッケージインポートを変更するだけで、@Delete ファインダーメソッドを作成することもできます。
import io.quarkus.hibernate.panache.PanacheRepository;
import jakarta.data.repository.Delete;
import jakarta.data.repository.Find;
import jakarta.data.repository.Query;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import java.time.LocalDate;
import java.util.List;
@Entity
public class Cat {
@Id
@GeneratedValue
public Long id;
public String name;
public LocalDate birth;
public Breed breed;
public enum Breed {
CUTE, HAIRLESS;
}
public interface Repo extends PanacheRepository.Stateless<Cat, Long> {
@Find (1)
Cat findByName(String name);
@Query("where breed = CUTE") (2)
List<Cat> findCute();
@Delete (3)
long deleteByName(String name);
@Query("delete from Cat where breed = HAIRLESS") (4)
long deleteHairless();
}
}
| 1 | これは通常のファインダーメソッドです |
| 2 | @HQL の代わりに @Query を使用します。これらはすべて同じです。 |
| 3 | delete ファインダーメソッドを記述できます。これはファインダーメソッドと似ていますが、そのアクションはエンティティを削除することであり、オプションで削除された数を返します。 |
| 4 | 削除メソッドは常にクエリメソッドとして明示的に記述できます。 |
しかし、それ以外の場合でも、エンティティを PanacheEntity またはそのバリアントのいずれかを拡張させることができ、リポジトリを注入したり、Cat_.repo() アクセサーを介して取得したりすることができます。
これらの機能の詳細については、対応する Hibernate Data Repositories および Jakarta Data ガイドを参照してください。
リポジトリメソッドから Uni 型を返すことでリアクティブなバリアントを使用することもできますし、マネージドエンティティをサポートするために、エンティティに他のタイプのリポジトリを追加することもできます。
|
| Panache ステートレスリポジトリ または Panache ステートレスリアクティブリポジトリ を使用することもできます。 |
REST integration
When your application uses both quarkus-data-hibernate and quarkus-rest-jackson, Quarkus automatically
adds REST support for common Jakarta Data parameter types. This lets you declare Jakarta Data types directly as
REST endpoint parameters, and they will be populated from HTTP query parameters without any manual parsing.
Supported parameter types
The following Jakarta Data types are automatically mapped from query parameters:
| Jakarta Data type | クエリーパラメーター | デフォルト | Example URL |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For Sort and Order, prefix the property name with - to indicate descending order.
Cursor-based pagination (PageRequest.Mode.CURSOR_NEXT / CURSOR_PREVIOUS) is not currently supported
by this REST integration. Only offset-based pagination is available via query parameters.
|
Page serialization
When a REST endpoint returns a jakarta.data.page.Page, it is serialized as a JSON object with pagination
metadata, rather than as a plain array:
{
"content": [ ... ],
"hasNext": true,
"hasPrevious": false,
"totalElements": 100,
"totalPages": 34
}
The totalElements and totalPages fields are only included when the PageRequest was created with
requestTotal=true.
Example endpoint
Here is an example of a REST endpoint that uses PageRequest and Order to return a paginated, sorted
list of cats. PanacheRepository already provides a findAll(PageRequest, Order) method, so you can use
the entity and repository we defined earlier directly:
import jakarta.data.Order;
import jakarta.data.page.Page;
import jakarta.data.page.PageRequest;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("cats")
public class CatResource {
@Inject
Cat.Repo repo;
@GET
public Page<Cat> list(PageRequest pageRequest, Order<Cat> order) { (1)
return repo.findAll(pageRequest, order);
}
}
| 1 | PageRequest is populated from page, size, and requestTotal query parameters.
Order is populated from repeating sort query parameters. |
A client can then call:
GET /cats?page=1&size=20&sort=name&sort=-birth
This produces a PageRequest.ofPage(1, 20, true) and Order.by(Sort.asc("name"), Sort.desc("birth")).
HQL, JD-QL, Jakarta-QL, Panache-QL
この API では、異なるクエリ言語が使用されています。
-
Hibernate Query Language (HQL) は、Hibernate で使用されるクエリ言語であり、
@HQLアノテーションによってサポートされます。 -
Jakarta Persistence Query Language (JP-QL) は、Hibernate Query Language のサブセットです。これは現在、Jakarta Query と呼ばれる独自の仕様に移行中です。
-
Jakarta Data Query Language (JD-QL) は、Jakarta Persistence Query Language のサブセットであり、
@Queryアノテーションによってサポートされます。 -
Panache Query Language は、Hibernate Query Language のスーパーセットであり、ごくわずかなショートカットを追加したもので、すべての
PanacheRepositoryクエリでサポートされています。
Panache クエリー言語
通常、ほとんどのHQLクエリは、from EntityName [where …] [order by …] という形式で、最後にオプションの要素が続きます。
選択クエリーが from、 select、または with で始まっていない場合は、次の追加形式がサポートされます。
-
order by …はfrom EntityName order by …に展開されます -
<singleAttribute>` (および単一のパラメーター) は
from EntityName where <singleAttribute> = ?に展開されます -
where <query>はfrom EntityName where <query>に展開されます -
<query>はfrom EntityName where <query>に展開されます
更新クエリーが update で始まらない場合は、以下の追加の形式をサポートしています:
-
from EntityName …` は
update EntityName …に展開されます -
set? <singleAttribute>(および単一のパラメーター) はupdate EntityName set <singleAttribute> = ?に展開されます -
set? <update-query>はupdate EntityName set <update-query>に展開されます
削除クエリーが delete で始まらない場合は、以下の追加の形式をサポートしています:
-
from EntityName …はdelete from EntityName …に展開されます -
<singleAttribute>(および単一のパラメーター) はdelete from EntityName where <singleAttribute> = ?に展開されます -
<query>はdelete from EntityName where <query>に展開されます
Secure Panache repositories
Quarkus Security provides an initial support for securing Panache Repositories with security annotations.
@Entity
public class Cat extends PanacheEntity {
public String name;
public interface SecuredRepo {
@PermissionsAllowed("cat:list")
@PermissionsAllowed("cat:read")
@Find
List<Cat> findByName(String name);
}
}
@RolesAllowed("admin")
public interface SecuredCatRepo extends PanacheManagedBlockingRepository<Cat> {
@Find
List<Cat> findByName(String name);
}
|
In the example above, only methods directly declared on the |
|
現在、型変数やワイルドカードを使用する汎用インターフェースメソッドは、標準のセキュリティーアノテーションでは確実には保護できません。したがって、そのようなメソッドを保護しようとするのではなく、以下に説明する 2 つの代替案のいずれかを使用する必要があります。 型変数を使用した汎用メソッドを持つリポジトリーの例
代替案 1: REST レイヤーの呼び出し元メソッドにセキュリティーアノテーションを適用する。
代替案 2: リポジトリーインターフェースの型変数
|