Panache Next によってシンプルになった Hibernate
experimentalQuarkus で Hibernate の使用を開始したい場合、Panache Next を使用した Hibernate は最適なソリューションです。
|
この技術は、experimentalと考えられています。 experimental モードでは、アイデアを成熟させるために早期のフィードバックが求められます。ソリューションが成熟するまでの間、プラットフォームの安定性や長期的な存在を保証するものではありません。フィードバックは メーリングリスト や GitHubの課題管理 で受け付けています。 とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。 |
| このエクステンションは現在実験段階にあるため、API が変更される可能性があります。特に、クラス名やパッケージ名、さらにはモジュール名も変更される可能性があります。現在、パブリックプレビュー化を進めており、皆様からのフィードバックを求めています。フィードバックは Zulip または GitHub issues までお寄せください。 |
ウォークスルー
Hibernate with Panache の使い方を学ぶために段階的なアプローチをとり、シンプルなエンティティから始めましょう。
このガイドでは、 Hibernate またはその基盤となる Jakarta Persistence 仕様 のどちらかの具体的な使用法については詳しく説明しません。なぜなら、どちらもすでに優れたドキュメントがあり、より深い知識を得るために活用できるからです。
エクステンションのインポートと設定
<!-- Import the Hibernate with Panache extension -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-panache-next</artifactId>
</dependency>
<!-- Pick your database driver -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
// Import the Hibernate with Panache extension
implementation("io.quarkus:quarkus-hibernate-panache-next")
// 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 ドライバー のいずれかを使用できます。 |
application.properties でデータソースを設定してください (ただし、開発モード の場合は必須ではありません。設定しない場合、dev services が提供されます)。
%prod.quarkus.datasource.username = hibernate
%prod.quarkus.datasource.password = hibernate
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db
最初のエンティティ
このガイドでは、Hibernate with Panache の基本的な使用法に焦点を当てるので、まずエンティティの作成方法から始めましょう。
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();
}
}
Panache を使用した Hibernate リポジトリは次のとおりです。
-
エンティティ内にネストされたインターフェース (ただし、好みに応じてトップレベルインターフェースでも構いません)
-
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
@GeneratedValue
public IdType id;
もちろん、独自のデータベース識別子を明示的に指定し、エンティティで 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 ステートレスリアクティブリポジトリ を使用することもできます。 |
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>に展開されます