§JPA を使用したデータベースへのアクセス
重要な注意
JPA Bean 検証は現在 Play 2.9 ではサポートされていません。詳細については、下記の詳細を参照してください。
§プロジェクトへの依存関係の追加
まず、プロジェクトが JDBC と JPA API の依存関係を提供する `javaJpa` に依存していることを Play に伝える必要があります。
Play には組み込みの JPA 実装はありません。利用可能な実装を選択できます。たとえば、Hibernate を使用するには、プロジェクトに次の依存関係を追加します。
libraryDependencies ++= Seq(
javaJpa,
"org.hibernate" % "hibernate-core" % "6.4.4.Final" // replace by your jpa implementation
)
§JNDI を介したデータソースの公開
JPA は、データソースが JNDI を介してアクセス可能であることを要求します。`conf/application.conf` にこの構成を追加することで、JNDI を介して任意の Play 管理データソースを公開できます。
db.default.jndiName=DefaultDS
データソースの構成方法の詳細については、データベース ドキュメント を参照してください。
§永続化ユニットの作成
次に、適切な `persistence.xml` JPA 構成ファイルを作成する必要があります。`conf/META-INF` ディレクトリに配置すると、クラスパスに正しく追加されます。
Hibernate で使用するサンプル構成ファイルを次に示します。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>DefaultDS</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
</properties>
</persistence-unit>
</persistence>
最後に、JPA プロバイダーで使用する永続化ユニットを Play に伝える必要があります。これは、`conf/application.conf` で `jpa.default` プロパティを設定することで行います。
jpa.default=defaultPersistenceUnit
§JPA を使用した Play のデプロイ
JPA を使用しながら開発モードで Play を実行しても問題ありませんが、アプリケーションをデプロイするには、これを `build.sbt` ファイルに追加する必要があります。
PlayKeys.externalizeResourcesExcludes += baseDirectory.value / "conf" / "META-INF" / "persistence.xml"
**注:** 外部化されたリソースの構成方法の詳細については、こちらを参照してください。
上記の構成により、`persistence.xml` ファイルは常に生成されたアプリケーション `jar` ファイルの**内部**に保持されます。
これは JPA 仕様 の要件です。それによると、`persistence.xml` ファイルは、その永続化ユニットのエンティティが存在する `jar` ファイルと同じ場所に存在する必要があります。そうでなければ、これらのエンティティは永続化ユニットで使用できなくなります。(ただし、`<jar-file>xxx.jar</jar-file>` を介してエンティティを含む `jar` ファイルを永続化ユニットに明示的に追加することはできますが、開発モードでは `jar` ファイルが生成されないため、Play では `FileNotFoundException` が発生してうまく機能しません。また、アプリケーションをデプロイする際に、生成されたアプリケーション `jar` ファイルの名前は、アプリケーションの現在のバージョンが追加されるため、新しいリリースごとに変更されます。)
§`play.db.jpa.JPAApi` の使用
Play は、エンティティマネージャー とトランザクションを操作するための便利な API を提供します。この API は `play.db.jpa.JPAApi` によって定義されており、次のコードのように他のオブジェクトに注入できます。
import jakarta.persistence.*;
import java.util.concurrent.*;
import javax.inject.*;
import play.db.jpa.JPAApi;
@Singleton
public class JPARepository {
private JPAApi jpaApi;
private DatabaseExecutionContext executionContext;
@Inject
public JPARepository(JPAApi api, DatabaseExecutionContext executionContext) {
this.jpaApi = api;
this.executionContext = executionContext;
}
}
JPA 操作を リポジトリ または DAO の背後に分離することをお勧めします。これにより、カスタム実行コンテキストとトランザクションを使用してすべての JPA 操作を管理できます。
つまり、すべての JPA 操作はインターフェースの背後で行われます。JPA クラスはパッケージプライベートであり、永続化対応オブジェクトがアプリケーションの他の部分に公開されることはありません。また、セッションは、非同期境界を定義するメソッド(つまり、`CompletionStage` を返すメソッド)を超えて開かれたままにはなりません。
これは、ドメインオブジェクト(DDD の用語では集約ルート)がリポジトリへの内部参照を持ち、JPA ベースの遅延読み込みを使用するのではなく、エンティティと値オブジェクトのリストを返すためにリポジトリを呼び出すことを意味する可能性があります。
§カスタム ExecutionContext の使用
**注:** Play のデフォルトのレンダリングスレッドプールを使用するアクションで JPA を直接使用すると、JDBC が実行中のスレッドをブロックするため、Play を非同期で使用する機能が制限されます。
JPA を使用する場合、常にカスタム実行コンテキストを使用し、Play のレンダリングスレッドプールがページのレンダリングに完全に集中し、コアを最大限に活用できるようにする必要があります。Play の `CustomExecutionContext` クラスを使用して、JDBC 操作専用の専用カスタム実行コンテキストを構成できます。詳細については、JavaAsync と ThreadPools を参照してください。
Play のダウンロードページ のブロッキング API(つまり、Anorm、JPA)を使用するすべての Play のサンプルテンプレートは、適切な場所でカスタム実行コンテキストを使用するように更新されています。たとえば、https://github.com/playframework/play-samples/tree/3.0.x/play-java-jpa-example にアクセスすると、JPAPersonRepository クラスがデータベース操作をすべてラップする `DatabaseExecutionContext` を取得していることがわかります。
JDBC 接続プールを含むスレッドプールのサイズ設定では、スレッドプールエグゼキュータを使用して、接続プールと一致する固定のスレッドプールサイズが必要です。HikariCP のプールサイズ設定ページ のアドバイスに従って、物理コア数の2倍、プラスディスクスピンドルの数に JDBC 接続プールの数を構成する必要があります。つまり、4コアの CPU と1つのディスクがある場合、プールには合計 9 つの JDBC 接続があります。
# db connections = ((physical_core_count * 2) + effective_spindle_count)
fixedConnectionPool = 9
database.dispatcher {
executor = "thread-pool-executor"
throughput = 1
thread-pool-executor {
fixed-pool-size = ${fixedConnectionPool}
}
}
§JPA トランザクションの実行
`JPAApi` は、JPA トランザクション内で任意のコードを実行するためのさまざまな `withTransaction(...)` メソッドを提供します。ただし、これらのメソッドにはカスタム実行コンテキストが含まれていないため、IO バインド実行コンテキストを使用して `CompletableFuture` 内でラップする必要があります。
§例
`JPAApi.withTransaction(Function<EntityManager, T>)` の使用
public CompletionStage<Long> runningWithTransaction() {
return CompletableFuture.supplyAsync(
() -> {
// lambda is an instance of Function<EntityManager, Long>
return jpaApi.withTransaction(
entityManager -> {
Query query = entityManager.createNativeQuery("select max(age) from people");
return (Long) query.getSingleResult();
});
},
executionContext);
}
`JPAApi.withTransaction(Consumer<EntityManager>)` を使用したバッチ更新の実行
public CompletionStage<Void> runningWithRunnable() {
// lambda is an instance of Consumer<EntityManager>
return CompletableFuture.runAsync(
() -> {
jpaApi.withTransaction(
entityManager -> {
Query query =
entityManager.createNativeQuery("update people set active = 1 where age > 18");
query.executeUpdate();
});
},
executionContext);
}
§Play 2.9 では Bean 検証が現在サポートされていません
JPA 仕様では検証モードが定義されています。このモードはユーザーによってオーバーライドされない場合(例:`<validation-mode>...</validation-mode>` `persistence.xml` 内、または プロパティ を介して)、デフォルトで `AUTO` になります。これは、Hibernate が 自動的に クラスパスで Bean 検証参照実装(Hibernate Validator など)の存在を検出し、検出された場合は自動的にその Bean 検証をアクティブ化することを意味します。実装が検出されない場合、検証は実行されません。ただし、モードが手動で `NONE` 以外の値に設定されている場合、例外がスローされます。
課題は、Play と Hibernate ORM 6+ にあります。このバージョンでは、Hibernate ORM は、`javax.validation` を引き続き使用しているため、クラスパス上の Hibernate Validator バージョン 6.x(Play が利用している)を検出しなくなりました。ただし、Hibernate ORM 6+ は `jakarta.validation` クラスのみを検出します。
Hibernate Validator 7.x は Jakarta に移行しましたが、Play でこのバージョンにアップグレードするのは現在困難です。Play はバインディングを支援するために Play-Java-Forms のライブラリを使用していますが、これらのライブラリの新しいバージョンのみが Jakarta を採用し、Hibernate Validator 7 をサポートしています。残念ながら、これらの新しいライブラリバージョンは Java 17 とのみ互換性があります。Play 2.9 は Java 11 を引き続きサポートしているため、アップグレードは実行不可能です。hibernate-validator のバージョンを 7.x に手動で上書きしようとすると、Play-Java-Form の機能が壊れます。
Bean Validation の欠如がご使用のユースケースで問題となる場合は、お知らせください。そうすることで、この課題に対処するための潜在的な解決策を探ることができます。
§Play データベース進化の有効化
データベース進化 を読んで、Play データベース進化が何に役立つのかを理解し、使用方法の設定手順に従ってください。
次へ: キャッシュの使用
このドキュメントに誤りを見つけた場合、このページのソースコードはこちらにあります。ドキュメントガイドラインを読んだ後、プルリクエストを自由に送ってください。ご質問やアドバイスがありましたら、コミュニティフォーラムでコミュニティとの会話を始めてください。