検索

第12章 Java 永続 API (JPA)

download PDF

12.1. Java 永続 API (JPA) について

Java Persistence API (JPA) は、Java オブジェクトまたはクラスとリレーショナルデータベース間でデータのアクセス、永続化、および管理を行うための Java 仕様です。この JPA 仕様では、透過オブジェクトまたはリレーショナルマッピングのパラダイムが考慮されます。オブジェクトまたはリレーショナル永続化メカニズムに必要な基本的な API とメタデータが標準化されます。

注記

JPA 自体は製品ではなく仕様にすぎません。それ自体では永続化やその他の処理を実行できません。JPA はインターフェースセットにすぎず、実装を必要とします。

12.2. Hibernate Core

Hibernate Core は、Java 言語のオブジェクトリレーショナルマッピングフレームワークです。これは、オブジェクト指向ドメインモデルをリレーショナルデータベースにマッピングするためのフレームワークを提供するため、アプリケーションはデータベースとの直接対話を回避できます。Hibernate では、直接的な永続データベースアクセスを高レベルオブジェクト処理関数に置き換えることにより、オブジェクトリレーショナルインピーダンスの不一致の問題が解決されます。

12.3. Hibernate EntityManager

Hibernate EntityManager を使用すると、Java Persistence 2.1 仕様で定義されたように、プラグラミングインターフェースとライフサイクルルールが実装されます。このラッパーを Hibernate Annotations とともに使用することにより、 成熟した Hibernate Core の上に完全な (およびスタンドアロンの) JPA 永続化ソリューションが実装されます。プロジェクトのビジネス上のニーズまたは技術的なニーズに応じて、これら 3 つすべて、JPA プログラミングインターフェースなしのアノテーション、または純粋なネイティブ Hibernate Core の組み合わせを使用できます。いつでも Hibernate ネイティブ API、または必要な場合はネイティブ JDBC および SQL を使用できます。また、JBoss EAP が完全な Java 永続化ソリューションとともに提供されます。

JBoss EAP は Java Persistence 2.1 仕様に完全準拠しています。また、Hibernate はこの仕様に追加機能を提供します。JPA と JBoss EAP を使用するには、JBoss EAP に同梱されている bean-validationgreeter、および kitchensink クイックスタートを参照してください。クイックスタートのダウンロードおよび実行方法については、クイックスタートサンプルの使用を参照してください。

JPA の永続化は EJB 3 またはより新しい CDI、Java Context and Dependency Injection などのコンテナーと特定のコンテナーの外部で実行されるスタンドアロン Java SE アプリケーションで利用できます。両方の環境で以下のプログラミングインターフェースとアーティファクトを利用できます。

EntityManagerFactory
エンティティーマネージャーファクトリーはエンティティーマネージャーインスタンスを提供し、すべてのインスタンスは、特定の実装などで定義されたのと同じデフォルト設定を使用するために同じデータベースに接続するよう設定されます。複数のエンティティーマネージャーファクトリーを準備して複数のデータストアにアクセスできます。このインターフェースはネイティブ Hibernate の SessionFactory に似ています。
EntityManager
EntityManager API は、特定の作業単位でデータベースにアクセスするために使用されます。また、永続エンティティーインスタンスを作成および削除してプライマリーキー ID でエンティティーを見つけたり、すべてのエンティティーに対してクエリーを実行したりするためにも使用されます。このインターフェースは、Hibernate のセッションに似ています。
永続コンテキスト
永続コンテキストは、永続エンティティー ID が一意のエンティティーインスタンスであるエンティティーインスタンスのセットです。永続コンテキスト内で、エンティティーインスタンスとそのライフサイクルは特定のエンティティーマネージャーによって管理されます。このコンテキストのスコープはトランザクションまたは拡張された作業単位のいずれかです。
永続ユニット
該当するエンティティーマネージャーで管理できるエンティティータイプのセットは、永続ユニットにより定義されます。永続ユニットは、アプリケーションで関連付けまたはグループ化され、単一データストアに対するマッピングで併置する必要があるすべてのクラスのセットを定義します。
コンテナー管理エンティティーマネージャー
ライフサイクルがコンテナーにより管理されるエンティティーマネージャー。
アプリケーション管理エンティティーマネージャー
ライフサイクルがアプリケーションにより管理されるエンティティーマネージャー。
JTA エンティティーマネージャー
JTA トランザクションに関与するエンティティーマネージャー。
リソースローカルエンティティーマネージャー
リソーストランザクション (JTA トランザクションではない) を使用するエンティティーマネージャー。

12.4. 単純な JPA アプリケーションの作成

Red Hat Developer Studio で単純な JPA アプリケーションを作成する場合は、以下の手順を実行します。

  1. JBoss Developer Studio で JPA プロジェクトを作成します。

    1. Red Hat JBoss Developer Studio で、File-→ New -→ Project をクリックします。リストで JPA を見つけ、展開し、JPA Project を選択します。以下のダイアログが表示されます。

      図12.1 新規 JPA プロジェクトダイアログ

      This is the new JPA Project dialog.
    2. プロジェクト名を入力します。
    3. ターゲットランタイムを選択します。ターゲットランタイムがない場合は、Define New Server を使用した JBoss EAP Server の追加 に記載された手順に従って新しいサーバーとランタイムを定義します。
    4. JPA version (JPA バージョン) de 2.1 が選択されていることを確認します。
    5. Configuration (設定)Basic JPA Configuration (基本的な JPA 設定) を選択します。
    6. Finish をクリックします。
    7. 要求されたら、このタイプのプロジェクトを JPA パースペクティブウインドウに関連付けるかどうかを選択します。
  2. 新しい永続性設定ファイルを作成および設定します。

    1. Red Hat JBoss Developer Studio で EJB 3.x プロジェクトを開きます。
    2. Project Explorer (プロジェクトエクスプローラー) パネルでプロジェクトルートディレクトリーを右クリックします。
    3. New (新規) Other (その他)…​. を選択します。
    4. XML フォルダーから XML File (XML ファイル) を選択し、Next (次へ) をクリックします。
    5. 親ディレクトリーとして ejbModule/META-INF/ フォルダーを選択します。
    6. ファイルの名前を persistence.xml と指定し、Next (次へ) をクリックします。
    7. Create XML file from an XML schema file (XML スキーマファイルから XML ファイルを作成) を選択し、Next (次へ) をクリックします。
    8. Select XML Catalog entry (XML カタログエントリーの選択) リストから http://java.sun.com/xml/ns/persistence/persistence_2.0.xsd を選択し、Next (次へ) をクリックします。

      図12.2 永続 XML スキーマ

      Persistence XML Schema
    9. Finish (完了) をクリックしてファイルを作成します。persistence.xmlMETA-INF/ フォルダーに作成され、設定可能な状態になります。

      永続設定ファイルの例

      <persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">
         <persistence-unit name="example" transaction-type="JTA">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>
            <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
            <mapping-file>ormap.xml</mapping-file>
            <jar-file>TestApp.jar</jar-file>
            <class>org.test.Test</class>
            <shared-cache-mode>NONE</shared-cache-mode>
            <validation-mode>CALLBACK</validation-mode>
            <properties>
               <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
               <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
            </properties>
         </persistence-unit>
      </persistence>

12.5. Configuration (設定)

12.5.1. Hibernate 設定プロパティー

表12.1 Hibernate Java プロパティー
プロパティー名説明

hibernate.dialect

Hibernate の org.hibernate.dialect.Dialect のクラス名。Hibernate で、特定のリレーショナルデータベースに最適化された SQL を生成できるようになります。

ほとんどのケースで、Hibernate は、JDBC ドライバーにより返された JDBC メタデータに基づいて正しい org.hibernate.dialect.Dialect 実装を選択できます。

hibernate.show_sql

ブール変数。SQL ステートメントをすべてコンソールに書き込みます。これは、ログカテゴリー org.hibernate.SQLdebug に設定することと同じです。

hibernate.format_sql

ブール変数。SQL をログとコンソールにプリティプリントします。

hibernate.default_schema

修飾されていないテーブル名を、生成された SQL の該当するスキーマ/テーブルスペースで修飾します。

hibernate.default_catalog

修飾されていないテーブル名を、生成された SQL の該当するカタログで修飾します。

hibernate.session_factory_name

org.hibernate.SessionFactory が、作成後に JNDI のこの名前に自動的にバインドされます。たとえば、jndi/composite/name のようになります。

hibernate.max_fetch_depth

シングルエンドのアソシエーション (1 対 1 や多対 1 など) に対して外部結合フェッチツリーの最大の深さを設定します。0 を設定するとデフォルトの外部結合フェッチが無効になります。推奨値は、0 から 3 までの値です。

hibernate.default_batch_fetch_size

関連付けの Hibernate 一括フェッチに対するデフォルトサイズを設定します。推奨値は、48、および 16 です。

hibernate.default_entity_mode

この SessionFactory から開かれたすべてのセッションに対するエンティティー表現のデフォルトモードを設定します。値には dynamic-mapdom4jpojo などがあります。

hibernate.order_updates

ブール変数。Hibernate で、更新されるアイテムの主キー値で SQL 更新の順番付けを行います。これにより、高度な並列システムにおけるトランザクションデッドロックが軽減されます。

hibernate.generate_statistics

ブール変数。有効にすると、Hibernate がパフォーマンスのチューニングに役に立つ統計情報を収集します。

hibernate.use_identifier_rollback

ブール変数。有効にすると、オブジェクトが削除されたときに、生成された識別子プロパティーがデフォルト値にリセットされます。

hibernate.use_sql_comments

ブール変数。有効にすると、デバッグを簡単にするために Hibernate が SQL 内にコメントを生成します。デフォルト値は false です。

hibernate.id.new_generator_mappings

ブール値。@GeneratedValue を使用する場合に関係するプロパティーです。新しい IdentifierGenerator 実装が javax.persistence.GenerationType.AUTO、javax.persistence.GenerationType.TABLE、または javax.persistence.GenerationType.SEQUENCE に対して使用されるかどうかを示します。デフォルト値は false です。

hibernate.ejb.naming_strategy

Hibernate EntityManager を使用している場合は、org.hibernate.cfg.NamingStrategy 実装を選択します。Hibernate 5.0 では hibernate.ejb.naming_strategy はサポートされなくなりました。これが使用された場合は、分割された ImplicitNamingStrategy と PhysicalNamingStrategy に置き換わり、サポートが停止され、削除されたことを示す廃止メッセージがログに記録されます。

アプリケーションが EntityManager を使用しない場合は、Hibernate Reference Documentation - Naming Strategies の手順に従って NamingStrategy を設定します。

MetadataBuilder を使用し、暗黙的なネーミングストラテジーを適用するネイティブブートストラップの例については、Hibernate 5.0 ドキュメンテーションの http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#bootstrap-native-metadata を参照してください。物理的なネーミングストラテジーは MetadataBuilder.applyPhysicalNamingStrategy() を使用して適用できます。org.hibernate.boot.MetadataBuilder の詳細については、https://docs.jboss.org/hibernate/orm/5.0/javadocs/ を参照してください。

hibernate.implicit_naming_strategy

使用する org.hibernate.boot.model.naming.ImplicitNamingStrategy クラスを指定します。また、hibernate.implicit_naming_strategy を使用して ImplicitNamingStrategy を実装するカスタムクラスを設定することもできます。この設定には以下の短い名前が定義されています。

  • default - ImplicitNamingStrategyJpaCompliantImpl
  • jpa - ImplicitNamingStrategyJpaCompliantImpl
  • legacy-jpa - ImplicitNamingStrategyLegacyJpaImpl
  • legacy-hbm - ImplicitNamingStrategyLegacyHbmImpl
  • component-path - ImplicitNamingStrategyComponentPathImpl

デフォルト設定は、default の短い名前の ImplicitNamingStrategy によって定義されます。デフォルト設定が空白の場合、フォールバックは ImplicitNamingStrategyJpaCompliantImpl を使用します。

hibernate.physical_naming_strategy

データベースオブジェクト名に物理的なネーミングルールを適用するプラグ可能なストラテジーコントラクト。使用する PhysicalNamingStrategy クラスを指定します。デフォルトでは PhysicalNamingStrategyStandardImpl が使用されます。hibernate.physical_naming_strategy を使用して、PhysicalNamingStrategy を実装するカスタムクラスを設定することもできます。

重要

新しいアプリケーションでは、hibernate.id.new_generator_mappings のデフォルト値を true のままにする必要があります。Hibernate 3.3.x を使用した既存のアプリケーションが継続してシーケンスオブジェクトやテーブルベースのジェネレーターを使用し、後方互換性を維持する場合は、デフォルト値を false に変更する必要がある場合があります。

12.5.2. Hibernate JDBC と接続プロパティー

表12.2 プロパティー
プロパティー名説明

hibernate.jdbc.fetch_size

JDBC のフェッチサイズを判断するゼロでない値です (Statement.setFetchSize() を呼び出します)。

hibernate.jdbc.batch_size

Hibernate による JDBC2 バッチ更新の使用を有効にするゼロでない値です。推奨値は、530 です。

hibernate.jdbc.batch_versioned_data

ブール変数。JDBC ドライバーが executeBatch() から正しい行数を返す場合は、このプロパティーを true に設定します。Hibernate は自動的にバージョン化されたデータにバッチ処理された DML を使用します。デフォルト値は false です。

hibernate.jdbc.factory_class

カスタム org.hibernate.jdbc.Batcher を選択します。ほとんどのアプリケーションにはこの設定プロパティーは必要ありません。

hibernate.jdbc.use_scrollable_resultset

ブール変数。Hibernate による JDBC2 のスクロール可能な結果セットの使用を有効にします。このプロパティーはユーザーが提供した JDBC 接続を使用する場合にのみ必要です。その他の場合、Hibernate は接続メタデータを使用します。

hibernate.jdbc.use_streams_for_binary

ブール変数。システムレベルのプロパティーです。binary または serializable 型を JDBC へ読み書きしたり、JDBC から読み書きしたりする場合にストリームを使用します。

hibernate.jdbc.use_get_generated_keys

ブール変数。JDBC3 PreparedStatement.getGeneratedKeys() を使用して、挿入後にネイティブで生成された鍵を取得できるようにします。JDBC3+ ドライバーと JRE1.4+ が必要です。JDBC ドライバーに Hibernate 識別子ジェネレーターの問題がある場合は false に設定します。デフォルトでは、接続メタデータを使用してドライバーの機能を判断しようとします。

hibernate.connection.provider_class

JDBC 接続を Hibernate に提供するカスタム org.hibernate.connection.ConnectionProvider のクラス名です。

hibernate.connection.isolation

JDBC トランザクションの分離レベルを設定します。java.sql.Connection で意味のある値をチェックしますが、ほとんどのデータベースはすべての分離レベルをサポートするとは限らず、一部のデータベースは標準的でない分離を追加的に定義します。標準的な値は 1, 2, 4, 8 です。

hibernate.connection.autocommit

ブール変数。このプロパティーの使用は推奨されません。JDBC でプールされた接続に対して自動コミットを有効にします。

hibernate.connection.release_mode

Hibernate が JDBC 接続を解放するタイミングを指定します。デフォルトでは、セッションが明示的に閉じられるか切断されるまで JDBC 接続が保持されます。デフォルト値である auto では、JTA および CMT トランザクションストラテジーに対して after_statement が選択され、JDBC トランザクションストラテジーに対して after_transaction が選択されます。

利用可能な値は auto (デフォルト)、on_close、after_transactionafter_statement です。

この設定により、SessionFactory.openSession から返されたセッションのみが影響を受けます。 SessionFactory.getCurrentSession から取得されたセッションの場合は、使用のために設定された CurrentSessionContext 実装がこれらのセッションの接続リリースモードを制御します。

hibernate.connection.<propertyName>

JDBC プロパティー <propertyName>DriverManager.getConnection() に渡します。

hibernate.jndi.<propertyName>

プロパティー <propertyName> を JNDI InitialContextFactory に渡します。

12.5.3. Hibernate キャッシュプロパティー

表12.3 プロパティー
プロパティー名説明

hibernate.cache.region.factory_class

カスタム CacheProvider のクラス名。

hibernate.cache.use_minimal_puts

ブール変数です。2 次キャッシュの操作を最適化し、読み取りを増やして書き込みを最小限にします。これはクラスター化されたキャッシュで最も便利な設定であり、Hibernate 3 ではクラスター化されたキャッシュの実装に対してデフォルトで有効になっています。

hibernate.cache.use_query_cache

ブール変数です。クエリーキャッシュを有効にします。各クエリーをキャッシュ可能に設定する必要があります。

hibernate.cache.use_second_level_cache

ブール変数です。<cache> マッピングを指定するクラスに対してデフォルトで有効になっている 2 次 キャッシュを完全に無効にするため使用されます。

hibernate.cache.query_cache_factory

カスタム QueryCache インターフェースのクラス名です。デフォルト値は組み込みの StandardQueryCache です。

hibernate.cache.region_prefix

2 次キャッシュのリージョン名に使用する接頭辞です。

hibernate.cache.use_structured_entries

ブール変数です。人間が解読可能な形式でデータを 2 次キャッシュに保存するよう Hibernate を設定します。

hibernate.cache.default_cache_concurrency_strategy

@Cacheable または @Cache が使用される場合に使用するデフォルトの org.hibernate.annotations.CacheConcurrencyStrategy の名前を付与するため使用される設定です。このデフォルト値を上書するには、@Cache(strategy="..") を使用します。

12.5.4. Hibernate トランザクションプロパティー

表12.4 プロパティー
プロパティー名説明

hibernate.transaction.factory_class

Hibernate Transaction API と使用する TransactionFactory のクラス名です。デフォルト値は JDBCTransactionFactory です。

jta.UserTransaction

アプリケーションサーバーから JTA UserTransaction を取得するために JTATransactionFactory により使用される JNDI 名。

hibernate.transaction.manager_lookup_class

TransactionManagerLookup のクラス名。JVM レベルのキャッシングが有効になっている場合や、JTA 環境の hilo ジェネレーターを使用する場合に必要です。

hibernate.transaction.flush_before_completion

ブール変数。有効な場合、トランザクションの完了前フェーズの間にセッションが自動的にフラッシュされます。ビルトインおよび自動セッションコンテキスト管理が推奨されます。

hibernate.transaction.auto_close_session

ブール変数。有効な場合、トランザクションの完了後フェーズの間にセッションが自動的に閉じられます。ビルトインおよび自動セッションコンテキスト管理が推奨されます。

12.5.5. その他の Hibernate プロパティー

表12.5 プロパティー
プロパティー名説明

hibernate.current_session_context_class

「現在」の Session のスコープに対するカスタムストラテジーを提供します。値には jtathreadmanagedcustom.Class があります。

hibernate.query.factory_class

org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory または org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory の HQL パーサー実装を選択します。

hibernate.query.substitutions

Hibernate クエリーのトークンと SQL トークンとのマッピングに使用します (トークンは関数名またはリテラル名である場合があります)。たとえば、hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC のようになります。

hibernate.hbm2ddl.auto

SessionFactory が作成されると、スキーマ DDL を自動的に検証し、データベースにエクスポートします。create-drop を使用すると、 SessionFactory が明示的に閉じられたときにデータベーススキーマが破棄されます。プロパティー値のオプションは、validateupdatecreatecreate-drop です。

hibernate.hbm2ddl.import_files

SessionFactory の作成中に実行される SQL DML ステートメントが含まれる任意のファイルの名前 (コンマ区切り)。テストやデモに便利です。たとえば、INSERT ステートメントを追加すると、デプロイ時に最小限のデータセットがデータベースに入力されます。値の例としては、/humans.sql,/dogs.sql のようになります。

特定ファイルのステートメントは後続ファイルのステートメントの前に実行されるため、ファイルの順番に注意する必要があります。これらのステートメントはスキーマが作成された場合のみ実行されます (hibernate.hbm2ddl.autocreate または create-drop に設定された場合など)。

hibernate.hbm2ddl.import_files_sql_extractor

カスタム ImportSqlCommandExtractor のクラス名。デフォルト値は組み込みの SingleLineSqlCommandExtractor です。各インポートファイルから単一の SQL ステートメントを抽出する専用のパーサーを実装する時に便利です。Hibernate は、複数行にまたがる命令/コメントおよび引用符で囲まれた文字列をサポートする MultipleLinesSqlCommandExtractor も提供します (各ステートメントの最後にセミコロンが必要です)。

hibernate.bytecode.use_reflection_optimizer

ブール値。hibernate.cfg.xml ファイルで設定できないシステムレベルのプロパティーです。ランタイムリフレクションの代わりにバイトコード操作の使用を有効にします。リフレクションは、トラブルシューティングを行うときに役に立つ場合があります。オプティマイザーが無効の場合でも Hibernate には cglib または javassist が常に必要です。

hibernate.bytecode.provider

javassist または cglib をバイト操作エンジンとして使用することができます。デフォルトでは javassist が使用されます。プロパティー値は javassist または cglib のいずれかです。

12.5.6. Hibernate SQL 方言

重要

hibernate.dialect プロパティーをアプリケーションデータベースの適切な org.hibernate.dialect.Dialect サブクラスに設定する必要があります。方言が指定されている場合、Hibernate は他のプロパティーの一部に実用的なデフォルトを使用します。そのため、これらのプロパティーを手作業で指定する必要はありません。

表12.6 SQL 方言 (hibernate.dialect)
RDBMS方言

DB2

org.hibernate.dialect.DB2Dialect

DB2 AS/400

org.hibernate.dialect.DB2400Dialect

DB2 OS390

org.hibernate.dialect.DB2390Dialect

Firebird

org.hibernate.dialect.FirebirdDialect

FrontBase

org.hibernate.dialect.FrontbaseDialect

H2 Database

org.hibernate.dialect.H2Dialect

HypersonicSQL

org.hibernate.dialect.HSQLDialect

Informix

org.hibernate.dialect.InformixDialect

Ingres

org.hibernate.dialect.IngresDialect

Interbase

org.hibernate.dialect.InterbaseDialect

MariaDB 10

org.hibernate.dialect.MySQL57InnoDBDialect

Mckoi SQL

org.hibernate.dialect.MckoiDialect

Microsoft SQL Server 2000

org.hibernate.dialect.SQLServerDialect

Microsoft SQL Server 2005

org.hibernate.dialect.SQLServer2005Dialect

Microsoft SQL Server 2008

org.hibernate.dialect.SQLServer2008Dialect

Microsoft SQL Server 2012

org.hibernate.dialect.SQLServer2012Dialect

Microsoft SQL Server 2014

org.hibernate.dialect.SQLServer2012Dialect

MySQL5

org.hibernate.dialect.MySQL5Dialect

MySQL5.7

org.hibernate.dialect.MySQL57InnoDBDialect

InnoDB を用いる MYSQL5

org.hibernate.dialect.MySQL5InnoDBDialect

MyISAM を用いる MySQL

org.hibernate.dialect.MySQLMyISAMDialect

Oracle (全バージョン)

org.hibernate.dialect.OracleDialect

Oracle 9i

org.hibernate.dialect.Oracle9iDialect

Oracle 10g

org.hibernate.dialect.Oracle10gDialect

Oracle 11g

org.hibernate.dialect.Oracle10gDialect

Oracle 12c

org.hibernate.dialect.Oracle12cDialect

Pointbase

org.hibernate.dialect.PointbaseDialect

PostgreSQL

org.hibernate.dialect.PostgreSQLDialect

PostgreSQL 9.2

org.hibernate.dialect.PostgreSQL9Dialect

PostgreSQL 9.3

org.hibernate.dialect.PostgreSQL9Dialect

PostgreSQL 9.4

org.hibernate.dialect.PostgreSQL94Dialect

Postgres Plus Advanced Server

org.hibernate.dialect.PostgresPlusDialect

Progress

org.hibernate.dialect.ProgressDialect

SAP DB

org.hibernate.dialect.SAPDBDialect

Sybase

org.hibernate.dialect.SybaseASE15Dialect

Sybase 15.7

org.hibernate.dialect.SybaseASE157Dialect

Sybase Anywhere

org.hibernate.dialect.SybaseAnywhereDialect

12.6. 2 次キャッシュ

12.6.1. 2 次キャッシュ

2 次キャッシュとは、アプリケーションセッション以外で永続的に情報を保持するローカルのデータストアのことです。このキャッシュは永続プロバイダーにより管理されており、アプリケーションとデータを分けることでランタイム効率の改善をはかることができます。

JBoss EAP では、以下の目的のためにキャッシュがサポートされます。

  • Web セッションのクラスタリング
  • ステートフルセッション Bean のクラスタリング
  • SSO クラスタリング
  • Hibernate 2 次キャッシュ

各キャッシュコンテナーは 「repl」 と 「dist」 キャッシュを定義します。これらのキャッシュは、ユーザーアプリケーションで直接使用しないでください。

12.6.2. Hibernate 用 2 次キャッシュの設定

Hibernate 向けの 2 次レベルキャッシュとして動作する Infinispan の設定は、以下の 2 つの方法で行なえます。

  • Hibernate ネイティブアプリケーション経由 (hibernate.cfg.xml を使用)
  • JPA アプリケーション経由 (persistence.xml を使用)
Hibernate ネイティブアプリケーションを使用した Hibernate 用 2 次キャッシュの設定
  1. デプロイメントのクラスパスに hibernate.cfg.xml を作成します。
  2. 以下の XML を hibernate.cfg.xml に追加します。XML は <session-factory> タグ内に存在する必要があります。

    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.use_query_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.jboss.as.jpa.hibernate5.infinispan.InfinispanRegionFactory</property>
JPA ネイティブアプリケーションを使用した Hibernate 用 2 次キャッシュの設定
  1. Red Hat JBoss Developer Studio で Hibernate 設定ファイルを作成する方法については、単純な JPA アプリケーションの作成を参照してください。
  2. 以下の内容を persistence.xml ファイルに追加します。

    <persistence-unit name="...">
      (...) <!-- other configuration -->
      <shared-cache-mode>$SHARED_CACHE_MODE</shared-cache-mode>
      <properties>
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.cache.use_query_cache" value="true" />
      </properties>
    </persistence-unit>
    注記

    $SHARED_CACHE_MODE の値には以下のものがあります。

    • ALL - すべてのエンティティーがキャッシュ可能である見なされます。
    • ENABLE_SELECTIVE - キャッシュ可能とマークされたエンティティーのみがキャッシュ可能であると見なされます。
    • DISABLE_SELECTIVE - キャッシュ不可と明示的に示されたものを除くすべてのエンティティーはキャッシュ可能と見なされます。

12.7. Hibernate アノテーション

org.hibernate.annotations パッケージには、標準的な JPA アノテーション以外に Hibernate により提供される一部のアノテーションが含まれます。

表12.7 一般的なアノテーション
アノテーション説明

Check

クラス、プロパティー、コレクションのいずれかのレベルで定義できる任意の SQL チェック制約です。

Immutable

エンティティーまたはコレクションを不変としてマーク付けします。アノテーションがない場合、要素は可変となります。

不変のエンティティーはアプリケーションによって更新されないことがあります。不変エンティティーへの更新は無視されますが、例外は発生しません。

@Immutable をコレクションに付けるとコレクションは不変になるため、コレクションからの追加や削除およびコレクションへの追加や削除は許可されません。この結果、HibernateException が発生します。

表12.8 キャッシュエンティティー
アノテーション説明

Cache

ルートエンティティーまたはコレクションにキャッシングストラテジーを追加します。

表12.9 コレクション関連のアノテーション
アノテーション説明

MapKeyType

永続マップのキータイプを定義します。

ManyToAny

異なるエンティティータイプを参照する ToMany アソシエーションを定義します。 メタデータ識別子カラムを介してエンティティータイプの照合が行われます。このようなマッピングはできるだけ行わないでください。

OrderBy

SQL の順序付け (HQL の順序付けではない) を使用してコレクションの順序を付けます。

OnDelete

コレクションやアレイ、結合されたサブクラスの削除に使用されるストラテジーです。OnDelete の 2 次テーブルはサポートされていません。

Persister

カスタムパーシスターを指定します。

Sort

コレクションのソート (Java レベルのソート)。

Where

要素エンティティーまたはコレクションのターゲットエンティティーへ追加する where 節。この節は SQL で書かれます。

WhereJoinTable

コレクション結合テーブルへ追加する where 節。この節は SQL で書かれます。

表12.10 CRUD 操作用のカスタム SQL
アノテーション説明

Loader

Hibernate のデフォルトである FIND メソッドを上書きします。

SQLDelete

Hibernate のデフォルトである DELETE メソッドを上書きします。

SQLDeleteAll

Hibernate のデフォルトである DELETE ALL メソッドを上書きします。

SQLInsert

Hibernate のデフォルトである INSERT INTO メソッドを上書きします。

SQLUpdate

Hibernate のデフォルトである UPDATE メソッドを上書きします。

Subselect

不変の読み取り専用エンティティーを指定のサブセレクト表現へマッピングします。

Synchronize

自動フラッシュが適切に行われ、派生したエンティティーに対するクエリーが古いデータを返さないようにします。ほとんどの場合、Subselect と共に使用されます。

表12.11 Entity
アノテーション説明

Cascade

アソシエーションにカスケードストラテジーを適用します。

Entity

標準的な @Entity で定義されたもの以外に必要になることがあるメタデータを追加します。

  • mutable: このエンティティーが変更可能であるかどうか
  • dynamicInsert: 挿入に動的 SQL を許可する
  • dynamicUpdate: 更新に動的 SQL を許可する
  • selectBeforeUpdate: オブジェクトが実際に変更されない限り、Hibernate が SQL UPDATE を実行しないことを指定します。
  • polymorphism: エンティティーのポリモーフィズムが PolymorphismType.IMPLICIT (default) であるか、または PolymorphismType.EXPLICIT であるか
  • optimisticLock: オプティミスティックロッキングストラテジー (OptimisticLockType.VERSION、OptimisticLockType.NONE、OptimisticLockType.DIRTY、または OptimisticLockType.ALL)

    注記

    アノテーション「Entity」は非推奨になり、今後のリリースで削除される予定です。個々の属性または値はアノテーションにする必要があります。

Polymorphism

Hibernate がエンティティーの階層に適用する多様性タイプを定義するために使用されます。

Proxy

特定クラスのレイジーおよびプロキシ設定。

Table

1 次または 2 次テーブルへの補足情報。

Tables

Table の複数アノテーション。

Target

明示的なターゲットを定義し、リフレクションやジェネリクスで解決しないようにします。

Tuplizer

1 つのエンティティーまたはコンポーネントに対して単一の tuplizer を定義します。

Tuplizers

1 つのエンティティーまたはコンポーネントに対して tuplizer のセットを定義します。

表12.12 Fetching
アノテーション説明

BatchSize

SQL ローディングのバッチサイズ。

FetchProfile

フェッチングストラテジープロファイルを定義します。

FetchProfiles

@FetchProfile の複数アノテーション。

表12.13 Filters
アノテーション説明

Filter

エンティティーまたはコレクションのターゲットエンティティーにフィルターを追加します。

FilterDef

フィルター定義。

FilterDefs

フィルター定義のアレイ。

FilterJoinTable

結合テーブルのコレクションへフィルターを追加します。

FilterJoinTables

複数の @FilterJoinTable をコレクションへ追加します。

Filters

複数の @Filter を追加します。

ParamDef

パラメーターの定義。

表12.14 主キー
アノテーション説明

Generated

このアノテーション付けされたプロパティーはデータベースによって生成されます。

GenericGenerator

Hibernate ジェネレーターをデタイプ (detyped) で記述するジェネレーターアノテーションです。

GenericGenerators

汎用ジェネレーター定義のアレイ。

NaturalId

プロパティーがエンティティーのナチュラル ID の一部であることを指定します。

Parameter

キーと値のパターン。

RowId

Hibernate の ROWID マッピング機能をサポートします。

表12.15 継承
アノテーション説明

DiscriminatorFormula

ルートエントリーに置かれる識別子の公式です。

DiscriminatorOptions

Hibernate 固有の識別子プロパティーを表現する任意のアノテーションです。

MetaValue

該当する識別子の値を対応するエンティティータイプにマッピングします。

表12.16 JP-QL/HQL クエリーのマッピング
アノテーション説明

NamedNativeQueries

Hibernate NamedNativeQuery オブジェクトを保持するよう NamedNativeQueries を拡張します。

NamedNativeQuery

NamedNativeQuery を Hibernate の機能で拡張します。

NamedQueries

NamedQuery オブジェクトを保持するよう NamedQueries を拡張します。

NamedQuery

Hibernate の機能で NamedQuery を拡張します。

表12.17 単純なプロパティーのマッピング
アノテーション説明

AccessType

プロパティーのアクセスタイプ。

Columns

カラムのアレイをサポートします。コンポーネントユーザータイプのマッピングに便利です。

ColumnTransformer

カラムからの値の読み取りやカラムへの値の書き込みに使用されるカスタム SQL 表現です。直接的なオブジェクトのロードや保存、クエリーに使用されます。write 表現には必ず値に対して 1 つの 「?」 プレースホルダーが含まれなければなりません。

ColumnTransformers

@ColumnTransformer の複数アノテーションです。複数のカラムがこの挙動を使用する場合に便利です。

表12.18 プロパティー
アノテーション説明

Formula

ほとんどの場所で @Column の代替として使用されます。公式は有効な SQL フラグメントである必要があります。

Index

データベースのインデックスを定義します。

JoinFormula

ほとんどの場所で @JoinColumn の代替として使用されます。公式は有効な SQL フラグメントである必要があります。

Parent

所有者 (通常は所有するエンティティー) へのポインターとしてプロパティーを参照します。

Type

Hibernate のタイプ。

TypeDef

Hibernate タイプの定義。

TypeDefs

Hibernate タイプ定義のアレイ。

表12.19 単一アソシエーション関連のアノテーション
アノテーション説明

Any

複数のエンティティータイプを参照する ToOne を定義します。対応するエンティティータイプの照合は、メタデータ識別子カラムを介して行われます。このようなマッピングは最低限行う必要があります。

AnyMetaDef

@Any および @ManyToAny メタデータを定義します。

AnyMetaDefs

メタデータの @Any および @ManyToAny セットを定義します。エンティティーまたはパッケージレベルで定義できます。

Fetch

特定のアソシエーションに使用されるフェッチングストラテジーを定義します。

LazyCollection

コレクションのレイジー状態を定義します。

LazyToOne

ToOne アソシエーション (つまり、OneToOne または ManyToOne) のレイジーステータスを定義します。

NotFound

アソシエーション上で要素が見つからなかった時に実行するアクションです。

表12.20 オプティミスティックロッキング
アノテーション説明

OptimisticLock

アノテーション付けされたプロパティーの変更によってエンティティーのバージョン番号が増加するかどうか。アノテーション付けされていない場合、プロパティーは楽観的ロックストラテジー (デフォルト) に関与します。

OptimisticLocking

エンティティーに適用される楽観的ロックのスタイルを定義するために使用されます。階層ではルートエンティティーのみに有効です。

Source

バージョンおよびタイムスタンプバージョンプロパティーと併用するのに最適なアノテーションです。アノテーション値はタイムスタンプが生成される場所を決定します。

12.8. Hibernate クエリー言語

12.8.1. Hibernate クエリー言語

JPQL の概要

Java Persistence Query Language (JPQL) は、Java Persistence API (JPA) 仕様の一部として定義されたプラットフォーム非依存のオブジェクト指向クエリー言語です。JPQL は、リレーショナルデータベースに格納されたエンティティーに対してクエリーを実行するために使用されます。この言語は SQL から大きな影響を受けており、クエリーの構文は SQL クエリーに類似しますが、データベーステーブルと直接動作するのではなく JPA エンティティーオブジェクトに対して動作します。

HQL の概要

Hibernate Query Language (HQL) は、強力なクエリー言語であり、見た目は SQL に似ています。ただし、SQL と比較して、HQL は完全なオブジェクト指向であり、継承、ポリモーフィズム、アソシエーションなどの概念を理解します。

HQL は JPQL のスーパーセットです。HQL クエリーは有効な JPQL クエリーでないこともありますが、JPQL クエリーは常に有効な HQL クエリーになります。

HQL と JPQL は共にタイプセーフでないクエリー操作を実行します。基準 (criteria) クエリーがタイプセーフなクエリーを提供します。

12.8.2. HQL ステートメントについて

HQL と JPQL では、SELECTUPDATE、および DELETE ステートメントを使用できます。HQL では、SQL の INSERT-SELECT に似た形式で INSERT ステートメントも使用できます。

重要

更新操作または削除操作を一括で実行する場合は、アクティブな永続コンテキストでデータベースとエンティティーとの間に不整合が発生することがあるため、注意が必要です。一般的に、一括の更新操作または削除操作は、新しい永続コンテキストのトランザクション内、またはこのような操作により状態が影響を受ける可能性があるエンティティーを取得する前またはアクセスする前にのみ実行します。

表12.21 HQL ステートメント
ステートメント説明

SELECT

HQL での SELECT ステートメントの BNF は以下のとおりです。

select_statement :: =
        [select_clause]
        from_clause
        [where_clause]
        [groupby_clause]
        [having_clause]
        [orderby_clause]

UDPATE

HQL の UPDATE ステートメントの BNF は JPQL と同じです。

DELETE

HQL の DELETE ステートメントの BNF は JPQL と同じです。

12.8.3. INSERT ステートメント

HQL は INSERT ステートメントを定義する機能を追加します。これに相当するステートメントは JPQL にはありません。HQL の INSERT ステートメントの BNF は次のとおりです。

insert_statement ::= insert_clause select_statement

insert_clause ::= INSERT INTO entity_name (attribute_list)

attribute_list ::= state_field[, state_field ]*

attribute_list は、SQL INSERT ステートメントの column specification と似ています。マップされた継承に関係するエンティティーでは、名前付きエンティティー上で直接定義された属性のみを attribute_list で使用することが可能です。スーパークラスプロパティーは許可されず、サブクラスプロパティーは意味がありません。よって、 INSERT ステートメントは本質的に非多形となります。

警告

The select_statement can be any valid HQL select query, with the caveat that the return types must match the types expected by the insert. Currently, this is checked during query compilation rather than allowing the check to relegate to the database. This can cause problems with Hibernate Types that are equivalent as opposed to equal. For example, this might cause mismatch issues between an attribute mapped as an org.hibernate.type.DateType and an attribute defined as a org.hibernate.type.TimestampType, even though the database might not make a distinction or might be able to handle the conversion.

insert ステートメントは id 属性に対して 2 つのオプションを提供します。1 つ目は、id 属性を attribute_list に明示的に指定するオプションです。この場合、値は対応する select 式から取得されます。2 つ目は attribute_list に指定しないオプションであり、この場合生成された値が使用されます。2 つ目のオプションは、「データベース内」で動作する id ジェネレーターを使用する場合のみ選択可能です。このオプションを「インメモリ」タイプのジェネレーターで使用しようとすると、構文解析中に例外が生じます。

insert ステートメントは楽観的ロックの属性に対しても 2 つのオプションを提供します。1 つ目は attribute_list に属性を指定するオプションです。この場合、値は対応する select 式から取得されます。2 つ目は attribute_list に指定しないオプションです。この場合、対応する org.hibernate.type.VersionType によって定義される seed value が使用されます。

例: INSERT クエリーステートメント

String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();

12.8.4. FROM 節

FROM 節の役割は、他のクエリーが使用できるオブジェクトモデルタイプの範囲を定義することです。また、他のクエリーが使用できる「ID 変数」もすべて定義します。

12.8.5. WITH 節

HQL は WITH 節を定義し、結合条件を限定します。これは HQL に固有の機能で、JPQL はこの機能を定義しません。

例: With

select distinct c
from Customer c
    left join c.orders o
        with o.value > 5000.00

生成された SQL では、with clause の条件が生成された SQL の on clause の一部となりますが、本項の他のクエリーでは HQL/JPQL の条件が生成された SQL の where clause の一部となることが重要な違いです。この例に特有の違いは重要ではないでしょう。さらに複雑なクエリーでは、with clause が必要になることがあります。

明示的な結合は、アソシエーションまたはコンポーネント/埋め込み属性を参照することがあります。コンポーネント/埋め込み属性では、結合は論理的であり、物理 (SQL) 結合に関連しません。

12.8.6. HQL の順序付け

クエリーの結果を順序付けすることも可能です。ORDER BY 節を使用して、結果を順序付けするために使用される選択値を指定します。order-by 節の一部として有効な式タイプには以下が含まれます。

  • ステートフィールド
  • コンポーネント/埋め込み可能属性
  • 算術演算や関数などのスカラー式
  • 前述の式タイプのいずれかに対する select 節に宣言された ID 変数

HQL は、order-by 節で参照されたすべての値が select 節で名付けされることを強制しませんが、JPQL では必要となります。データベースの移植性を要求するアプリケーションは、select 節で参照されない order-by 節の参照値をサポートしないデータベースがあることを認識する必要があります。

order-by の各式は、ASC (昇順) または DESC (降順) で希望の順序を示すよう修飾することができます。

例: Order-by

// legal because p.name is implicitly part of p
select p
from Person p
order by p.name

select c.id, sum( o.total ) as t
from Order o
    inner join o.customer c
group by c.id
order by t

12.8.7. 一括更新、一括送信、および一括削除

Hibernate では、Data Manipulation Language (DML) を使用して、マップ済みデータベースのデータを直接、一括挿入、一括更新、および一括削除できます (Hibernate Query Language を使用)。

警告

DML を使用すると、オブジェクト/リレーショナルマッピングに違反し、オブジェクトの状態に影響が出ることがあります。オブジェクトの状態はメモリーでは変わりません。DML を使用することにより、基礎となるデータベースで実行された操作に応じて、メモリー内オブジェクトの状態は影響を受けません。DML を使用する場合、メモリー内データは注意を払って使用する必要があります。

UPDATE ステートメントと DELETE ステートメントの擬似構文:

( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?.

注記

FROM キーワードと WHERE Clause はオプションです。

UPDATE ステートメントまたは DELETE ステートメントの実行結果は、実際に影響 (更新または削除) を受けた行の数です。

例: 一括更新ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate = "update Company set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

例: 一括削除ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlDelete = "delete Company where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

Query.executeUpdate() メソッドにより返された int 値は、操作で影響を受けたデータベース内のエンティティー数を示します。

内部的に、データベースは複数の SQL ステートメントを使用して DML 更新または削除の要求に対する操作を実行することがあります。多くの場合、これは、更新または削除する必要があるテーブルと結合テーブル間に存在する関係のためです。

たとえば、上記の例のように削除ステートメントを発行すると、oldName で指定された会社用の Company テーブルだけでなく、結合テーブルに対しても削除が実行されることがあります。したがって、Employee テーブルとの関係が BiDirectional ManyToMany である Company テーブルで、以前の例の正常な実行結果として、対応する結合テーブル Company_Employee から複数の行が失われます。

上記の int deletedEntries 値には、この操作により影響を受けたすべての行 (結合テーブルの行を含む) の数が含まれます。

INSERT ステートメントの擬似構文は INSERT INTO EntityName properties_list select_statement です。

注記

INSERT INTO …​ SELECT …​ という形式のみサポートされ、INSERT INTO …​ VALUES …​ という形式はサポートされません。

例: 一括挿入ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlInsert = "insert into Account (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
        .executeUpdate();
tx.commit();
session.close();

SELECT ステートメントを介して id 属性の値を提供しない場合は、基礎となるデータベースが自動生成されたキーをサポートする限り、ユーザーに対して ID が生成されます。この一括挿入操作の戻り値は、データベースで実際に作成されたエントリーの数です。

12.8.8. コレクションメンバーの参照

コレクション値 (collection-valued) アソシエーションへの参照は、実際はコレクションのを参照します。

例: コレクションの参照

select c
from Customer c
    join c.orders o
    join o.lineItems l
    join l.product p
where o.status = 'pending'
  and p.status = 'backorder'

// alternate syntax
select c
from Customer c,
    in(c.orders) o,
    in(o.lineItems) l
    join l.product p
where o.status = 'pending'
  and p.status = 'backorder'

この例では、ID 変数 o が、Customer#orders アソシエーションの要素の型であるオブジェクトモデル型 Order を実際に参照します。

更にこの例には、IN 構文を使用してコレクションアソシエーション結合を指定する代替の構文があります。構文は両方同等です。アプリケーションが使用する構文は任意に選択できます。

12.8.9. 修飾パス式

コレクション値 (collection-valued) のアソシエーションは、実際にはそのコレクションのを参照すると前項で説明しました。コレクションのタイプを基に、明示的な修飾式のセットも使用可能です。

表12.22 修飾パス式
表現説明

VALUE

コレクション値を参照します。修飾子を指定しないことと同じです。目的を明示的に表す場合に便利です。コレクション値 (collection-valued) の参照のすべてのタイプに対して有効です。

INDEX

HQL ルールに基づき、マップキーまたはリストの場所 (OrderColumn の値) を参照するよう javax.persistence.OrderColumn アノテーションを指定するマップとリストに対して有効です。ただし、JPQL ではリストでの使用に対して確保され、マップに対して KEY が追加されます。JPA プロバイダーの移植性に関係するアプリケーションは、この違いを認識する必要があります。

KEY

マップに対してのみ有効です。マップのキーを参照します。キー自体がエンティティーである場合は、さらにナビゲートすることが可能です。

ENTRY

マップに対してのみ有効です。マップの論理 java.util.Map.Entry タプル (キーと値の組み合わせ) を参照します。ENTRY は終端パスとしてのみ有効であり、select 句のみで有効になります。

例: 修飾されたコレクションの参照

// Product.images is a Map<String,String> : key = a name, value = file path

// select all the image file paths (the map value) for Product#123
select i
from Product p
    join p.images i
where p.id = 123

// same as above
select value(i)
from Product p
    join p.images i
where p.id = 123

// select all the image names (the map key) for Product#123
select key(i)
from Product p
    join p.images i
where p.id = 123

// select all the image names and file paths (the 'Map.Entry') for Product#123
select entry(i)
from Product p
    join p.images i
where p.id = 123

// total the value of the initial line items for all orders for a customer
select sum( li.amount )
from Customer c
        join c.orders o
        join o.lineItems li
where c.id = 123
  and index(li) = 1

12.8.10. スカラー関数

HQL は、使用される基盤のデータに関係なく使用できる一部の標準的な機能を定義します。また、HQL は方言やアプリケーションによって定義された追加の機能も理解することができます。

12.8.11. HQL の標準化された関数について

使用される基盤のデータベースに関係なく HQL で使用できる関数は次のとおりです。

表12.23 HQL の標準化された関数
機能説明

BIT_LENGTH

バイナリーデータの長さを返します。

CAST

SQL キャストを実行します。キャストターゲットは使用する Hibernate マッピングタイプを命名する必要があります。

EXTRACT

datetime 値で SQL の抽出を実行します。抽出により、datetime 値の一部が抽出されます (年など)。以下の省略形を参照してください。

SECOND

秒を抽出する抽出の省略形。

MINUTE

分を抽出する抽出の省略形。

HOUR

時間を抽出する抽出の省略形。

DAY

日を抽出する抽出の省略形。

MONTH

月を抽出する抽出の省略形。

YEAR

年を抽出する抽出の省略形。

STR

値を文字データとしてキャストする省略形。

アプリケーション開発者は独自の関数セットを提供することもできます。通常、カスタム SQL 関数か SQL スニペットのエイリアスで表します。このような関数は、org.hibernate.cfg.Configuration の addSqlFunction メソッドを使用して宣言します。

12.8.12. 連結演算

HQL は、連結 (CONCAT) 関数をサポートするだけでなく、連結演算子も定義します。連結演算子は JPQL によっては定義されないため、移植可能なアプリケーションでは使用しないでください。連結演算子は SQL の連結演算子である || を使用します。

例: 連結操作

select 'Mr. ' || c.name.first || ' ' || c.name.last
from Customer c
where c.gender = Gender.MALE

12.8.13. 動的インスタンス化

select 節でのみ有効な特別な式タイプがありますが、Hibernate では「動的インスタンス化」と呼びます。JPQL はこの機能の一部をサポートし、「コンストラクター式」と呼びます。

例: 動的インスタンス化 - コンストラクター

select new Family( mother, mate, offspr )
from DomesticCat as mother
    join mother.mate as mate
    left join mother.kittens as offspr

Object[] に対処せずに、クエリーの結果として返されるタイプセーフの Java オブジェクトで値をラッピングします。クラス参照は完全修飾する必要があり、一致するコンストラクターがなければなりません。

ここでは、クラスをマッピングする必要はありません。エンティティーを表す場合、結果となるインスタンスは NEW 状態で返されます (管理されません)。

この部分は JPQL もサポートします。HQL は他の「動的インスタンス化」もサポートします。最初に、スカラーの結果に対して Object[] ではなくリストを返すよう、クエリーで指定できます。

例: 動的インスタンス化 - リスト

select new list(mother, offspr, mate.name)
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

このクエリーの結果は List<Object[]> ではなく List<List>

また、HQL はマップにおけるスカラーの結果のラッピングもサポートします。

例: 動的インスタンス化 - マップ

select new map( mother as mother, offspr as offspr, mate as mate )
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

select new map( max(c.bodyWeight) as max, min(c.bodyWeight) as min, count(*) as n )
from Cat cxt

このクエリーの結果は List<Object[]> ではなく List<Map<String,Object>> になります。マップのキーは select 式へ提供されたエイリアスによって定義されます。

12.8.14. HQL 述語

述語は where 句、having 句、および検索 case 式の基盤を形成します。これらは通常は TRUE または FALSE の真理値に解決される式ですが、一般的に NULL 値が関係するブール値の比較は UNKNOWN に解決されます。

HQL 述語
  • Null 述語

    NULL の値をチェックします。基本的な属性参照、エンティティー参照、およびパラメーターへ適用できます。HQL では、コンポーネント/埋め込み可能タイプにも適用できます。

    Null チェックの例

    // select everyone with an associated address
    select p
    from Person p
    where p.address is not null
    
    // select everyone without an associated address
    select p
    from Person p
      where p.address is null
  • LIKE 述語

    文字列値で LIKE 比較を実行します。構文は次のとおりです。

    like_expression ::=
           string_expression
           [NOT] LIKE pattern_value
           [ESCAPE escape_character]

    セマンティックは SQL の LIKE 式に従います。pattern_value は、string_expression で一致を試みるパターンです。SQL と同様に、pattern_value には _ (アンダースコア) と % (パーセント) をワイルドカードとして使用できます。意味も同じであり、_ はあらゆる 1 つの文字と一致し、% はあらゆる数の文字と一致します。

    任意の escape_character は、pattern_value_% をエスケープするために使用するエスケープ文字を指定するために使用されます。これは _% が含まれるパターンを検索する必要がある場合に役立ちます。

    Like 述語の例

    select p
    from Person p
    where p.name like '%Schmidt'
    
    select p
    from Person p
    where p.name not like 'Jingleheimmer%'
    
    // find any with name starting with "sp_"
    select sp
    from StoredProcedureMetadata sp
    where sp.name like 'sp|_%' escape '|'
  • BETWEEN 述語

    SQL の BETWEEN 式と同様です。値が他の 2 つの値の間にあることを評価するために実行します。演算対象はすべて比較可能な型を持つ必要があります。

    Between 述語の例

    select p
    from Customer c
        join c.paymentHistory p
    where c.id = 123
      and index(p) between 0 and 9
    
    select c
    from Customer c
    where c.president.dateOfBirth
            between {d '1945-01-01'}
                and {d '1965-01-01'}
    
    select o
    from Order o
    where o.total between 500 and 5000
    
    select p
    from Person p
    where p.name between 'A' and 'E'
  • IN 述語

    IN 述語は、値のリストに特定の値があることを確認するチェックを行います。構文は次のとおりです。

    in_expression ::= single_valued_expression
                [NOT] IN single_valued_list
    
    single_valued_list ::= constructor_expression |
                (subquery) |
                collection_valued_input_parameter
    
    constructor_expression ::= (expression[, expression]*)

    single_valued_expression のタイプと single_valued_list の各値は一致しなければなりません。JPQL は有効なタイプを文字列、数字、日付、時間、タイムスタンプ、列挙型に限定します。JPQL では、 single_valued_expression は下記のみを参照できます。

    • 簡単な属性を表す「ステートフィールド」。アソシエーションとコンポーネント/埋め込み属性を明確に除外します。
    • エンティティータイプの式。

      HQL では、single_valued_expression はさらに広範囲の式タイプを参照することが可能です。単一値のアソシエーションは許可されます。コンポーネント/埋め込み属性も許可されますが、この機能は、基礎となるデータベースのタプルまたは「行値コンストラクター構文」へのサポートのレベルに依存します。また、HQL は値タイプを制限しませんが、基礎となるデータベースのべンダーによってはサポートが制限されるタイプがあることをアプリケーション開発者は認識しておいたほうがよいでしょう。これが JPQL の制限の主な原因となります。

      値のリストは複数の異なるソースより取得することが可能です。constructor_expressioncollection_valued_input_parameter では、空の値のリストは許可されず、最低でも 1 つの値が含まれなければなりません。

      In 述語の例

      select p
      from Payment p
      where type(p) in (CreditCardPayment, WireTransferPayment)
      
      select c
      from Customer c
      where c.hqAddress.state in ('TX', 'OK', 'LA', 'NM')
      
      select c
      from Customer c
      where c.hqAddress.state in ?
      
      select c
      from Customer c
      where c.hqAddress.state in (
          select dm.state
          from DeliveryMetadata dm
          where dm.salesTax is not null
      )
      
      // Not JPQL compliant!
      select c
      from Customer c
      where c.name in (
          ('John','Doe'),
          ('Jane','Doe')
      )
      
      // Not JPQL compliant!
      select c
      from Customer c
      where c.chiefExecutive in (
          select p
          from Person p
          where ...
      )

12.8.15. 関係比較

比較には比較演算子 (=、>、>=、<、⇐、<>) の 1 つが関与します。また、HQL によって != は <>. と同義の比較演算子として定義されます。オペランドは同じ型でなければなりません。

例: 相対比較

// numeric comparison
select c
from Customer c
where c.chiefExecutive.age < 30

// string comparison
select c
from Customer c
where c.name = 'Acme'

// datetime comparison
select c
from Customer c
where c.inceptionDate < {d '2000-01-01'}

// enum comparison
select c
from Customer c
where c.chiefExecutive.gender = com.acme.Gender.MALE

// boolean comparison
select c
from Customer c
where c.sendEmail = true

// entity type comparison
select p
from Payment p
where type(p) = WireTransferPayment

// entity value comparison
select c
from Customer c
where c.chiefExecutive = c.chiefTechnologist

比較には、サブクエリー修飾子である ALLANYSOME も関与します。SOMEANY は同義です。

サブクエリーの結果にあるすべての値に対して比較が true である場合、ALL 修飾子は true に解決されます。サブクエリーの結果が空の場合は false に解決されます。

例: ALL サブクエリー比較修飾子

// select all players that scored at least 3 points
// in every game.
select p
from Player p
where 3 > all (
   select spg.points
   from StatsPerGame spg
   where spg.player = p
)

サブクエリーの結果にある値の一部 (最低でも 1 つ) に対して比較が true の場合、ANY または SOME 修飾子は true に解決されます。サブクエリーの結果が空である場合、false に解決されます。

12.9. Hibernate サービス

12.9.1. Hibernate サービス

サービスは、さまざまな機能タイプのプラグ可能な実装を Hibernate に提供するクラスです。サービスは特定のサービスコントラクトインターフェースの実装です。インターフェースはサービスロールとして知られ、実装クラスはサービス実装として知られています。通常、ユーザーはすべての標準的なサービスロールの代替実装へプラグインできます (オーバーライド)。また、サービスロールのベースセットを越えた追加サービスを定義できます (拡張)。

12.9.2. サービスコントラクト

マーカーインターフェース org.hibernate.service.Service を実装することがサービスの基本的な要件になります。Hibernate は基本的なタイプセーフのために内部でこのインターフェースを使用します。

起動と停止の通知を受け取るために、サービスは org.hibernate.service.spi.Startable および org.hibernate.service.spi.Stoppable インターフェースを任意で実装することもできます。その他に、JMX 統合が有効になっている場合に JMX でサービスを管理可能としてマーク付けする org.hibernate.service.spi.Manageable という任意のサービスコントラクトがあります。

12.9.3. サービス依存関係のタイプ

サービスは、以下の 2 つの方法のいずれかを使用して、他のサービスに依存関係を宣言できます。

@org.hibernate.service.spi.InjectService
単一のパラメーターを受け取るサービス実装クラスのメソッドと @InjectService アノテーションが付けられているメソッドは、他のサービスのインジェクションを要求していると見なされます。
デフォルトでは、メソッドパラメーターのタイプは、インジェクトされるサービスロールであると想定されます。パラメータータイプがサービスロールではない場合は、InjectServiceserviceRole 属性を使用してロールを明示的に指定する必要があります。
デフォルトでは、インジェクトされたサービスは必須のサービスであると見なされます。そのため、名前付けされた依存サービスがない場合は、起動に失敗します。インジェクトされるサービスが任意のサービスである場合は、InjectService の required 属性を false (デフォルト値は true) として宣言する必要があります。
org.hibernate.service.spi.ServiceRegistryAwareService
2 つ目の方法は、単一の injectServices メソッドを宣言する任意のサービスインターフェース org.hibernate.service.spi.ServiceRegistryAwareService をサービスが実装する方法です。
起動中、Hibernate は org.hibernate.service.ServiceRegistry 自体をこのインターフェースを実装するサービスにインジェクトします。その後、サービスは ServiceRegistry 参照を使用して、必要な他のサービスを見つけることができます。

12.9.4. サービスレジストリー

12.9.4.1. ServiceRegistry

サービス自体以外の中央サービス API は org.hibernate.service.ServiceRegistry インターフェースです。サービスレジストリーの主な目的は、サービスを保持および管理し、サービスへのアクセスを提供することです。

サービスレジストリーは階層的です。レジストリーのサービスは、同じレジストリーおよび親レジストリーにあるサービスへの依存や利用が可能です。

org.hibernate.service.ServiceRegistryBuilder を使用して org.hibernate.service.ServiceRegistry インスタンスをビルドします。

例: ServiceRegistryBuilder を使用した ServiceRegistry の作成

ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry );
    ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();

12.9.5. カスタムサービス

12.9.5.1. カスタムサービス

org.hibernate.service.ServiceRegistry がビルドされると、不変であると見なされます。サービス自体は再設定を許可することもありますが、ここで言う不変とはサービスの追加や置換を意味します。そのため org.hibernate.service.ServiceRegistryBuilder によって提供される別のロールにより、生成された org.hibernate.service.ServiceRegistry に格納されるサービスを微調整できるようになります。

カスタムサービスについて org.hibernate.service.ServiceRegistryBuilder に通知する方法は 2 つあります。

  • org.hibernate.service.spi.BasicServiceInitiator クラスを実装してサービスクラスの要求に応じた構築を制御し、addInitiator メソッドを介して org.hibernate.service.ServiceRegistryBuilder へ追加します。
  • サービスクラスをインスタンス化し、addService メソッドを介して org.hibernate.service.ServiceRegistryBuilder へ追加します。

サービスを追加する方法とイニシエーターを追加する方法はいずれも、レジストリーの拡張 (新しいサービスロールの追加) やサービスのオーバーライド (サービス実装の置換) に対して有効です。

例: ServiceRegistryBuilder を用いた既存サービスのカスタマーサービスへの置き換え

   ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry );
   registryBuilder.addService( JdbcServices.class, new FakeJdbcService() );
   ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();


   public class FakeJdbcService implements JdbcServices{

       @Override
       public ConnectionProvider getConnectionProvider() {
           return null;
       }

       @Override
       public Dialect getDialect() {
           return null;
       }

       @Override
       public SqlStatementLogger getSqlStatementLogger() {
           return null;
       }

       @Override
       public SqlExceptionHelper getSqlExceptionHelper() {
           return null;
       }

       @Override
       public ExtractedDatabaseMetaData getExtractedMetaDataSupport() {
           return null;
       }

       @Override
       public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
           return null;
       }

       @Override
       public ResultSetWrapper getResultSetWrapper() {
           return null;
       }
   }

12.9.6. ブートストラップレジストリー

12.9.6.1. ブートストラップレジストリー

ブートストラップレジストリーは、ほとんどの操作を行うために必ず必要になるサービスを保持します。主なサービスは ClassLoaderService (代表的な例) です。設定ファイルの解決にもクラスローディングサービス (リソースのルックアップ) へのアクセスが必要になります。通常の使用では、これがルートレジストリー (親なし) になります。

ブートストラップレジストリーのインスタンスは org.hibernate.service.BootstrapServiceRegistryBuilder クラスを使用してビルドされます。

12.9.6.2. BootstrapServiceRegistryBuilder の使用

例: BootstrapServiceRegistryBuilder の使用

BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
        // pass in org.hibernate.integrator.spi.Integrator instances which are not
        // auto-discovered (for whatever reason) but which should be included
        .with( anExplicitIntegrator )
        // pass in a class loader that Hibernate should use to load application classes
        .with( anExplicitClassLoaderForApplicationClasses )
        // pass in a class loader that Hibernate should use to load resources
        .with( anExplicitClassLoaderForResources )
        // see BootstrapServiceRegistryBuilder for rest of available methods
        ...
        // finally, build the bootstrap registry with all the above options
        .build();

12.9.6.3. BootstrapRegistry サービス

org.hibernate.service.classloading.spi.ClassLoaderService

Hibernate はクラスローダーと対話する必要がありますが、Hibernate (またはライブラリー) がクラスローダーと対話する方法は、アプリケーションをホストするランタイム環境によって異なります。クラスローディングの要件は、アプリケーションサーバー、OSGi コンテナー、およびその他のモジュラークラスローディングシステムによって限定されます。このサービスは、このような環境の複雑性の抽象化を Hibernate に提供しますが、単一のスワップ可能なコンポーネントを用いることも重要な点になります。

クラスローダーとの対話では、Hibernate に以下の機能が必要になります。

  • アプリケーションクラスを見つける機能
  • 統合クラスを見つける機能
  • リソース (プロパティーファイル、xml ファイルなど) を見つける機能
  • java.util.ServiceLoader をロードする機能
注記

現在、アプリケーションクラスをロードする機能と統合クラスをロードする機能は、サービス上の 1 つの「ロードクラス」機能として組み合わされていますが、今後のリリースで変更になる可能性があります。

org.hibernate.integrator.spi.IntegratorService

アプリケーション、アドオン、およびその他のモジュールは Hibernate と統合する必要があります。以前の方法では、各モジュールの登録を調整するためにコンポーネント (通常はアプリケーション) が必要でした。この登録は各モジュールのインタグレーターの代わりに実行されました。

このサービスはディスカバリーに重点を置きます。org.hibernate.service.classloading.spi.ClassLoaderService によって提供される標準の Java java.util.ServiceLoader 機能を使用して、org.hibernate.integrator.spi.Integrator コントラクトの実装を検出します。

インテグレーターは /META-INF/services/org.hibernate.integrator.spi.Integrator という名前のファイルを定義し、クラスパス上で使用できるようにします。

このファイルは java.util.ServiceLoader メカニズムによって使用され、org.hibernate.integrator.spi.Integrator インターフェースを実装するクラスの完全修飾名を 1 行に 1 つずつリストします。

12.9.7. SessionFactory レジストリー

12.9.7.1. SessionFactory レジストリー

すべてのレジストリータイプのインスタンスを指定の org.hibernate.SessionFactory のターゲットとして扱うことが最良の方法ですが、このグループのサービスのインスタンスは明示的に 1 つの org.hibernate.SessionFactory に属します。

違いは開始する必要があるタイミングになります。一般的に開始する org.hibernate.SessionFactory にアクセスする必要があります。この特別なレジストリーは org.hibernate.service.spi.SessionFactoryServiceRegistry です。

12.9.7.2. SessionFactory サービス

org.hibernate.event.service.spi.EventListenerRegistry

説明
イベントリスナーを管理するサービス。
イニシエーター
org.hibernate.event.service.internal.EventListenerServiceInitiator
実装
org.hibernate.event.service.internal.EventListenerRegistryImpl

12.9.8. インテグレーター

12.9.8.1. インテグレーター

org.hibernate.integrator.spi.Integrator の目的は、機能する SessionFactory のビルドプロセスに開発者がフックできるようにする簡単な手段を提供することです。org.hibernate.integrator.spi.Integrator インターフェースは、ビルドプロセスにフックできるようにする integrate と、終了する SessionFactory にフックできるようにする disintegrate の 2 つのメソッドを定義します。

注記

org.hibernate.cfg.Configuration の代わりに org.hibernate.metamodel.source.MetadataImplementor を受け入れるオーバーロード形式の integrate は、org.hibernate.integrator.spi.Integrator で定義される 3 つ目のメソッドになります。この形式は 5.0 で完了予定であった新しいメタモデルコードでの使用向けです。

IntegratorService によって提供されるディスカバリー以外に、BootstrapServiceRegistry のビルド時にアプリケーションはインテグレーターを手動で登録することができます。

12.9.8.2. インテグレーターのユースケース

現在、org.hibernate.integrator.spi.Integrator の主なユースケースは、イベントリスナーの登録とサービスの提供です (org.hibernate.integrator.spi.ServiceContributingIntegrator を参照)。5.0 では、オブジェクトとリレーショナルモデルとの間のマッピングを定義するメタモデルを変更できるようにするための拡張を計画しています。

例: イベントリスナーの登録

public class MyIntegrator implements org.hibernate.integrator.spi.Integrator {

    public void integrate(
            Configuration configuration,
            SessionFactoryImplementor sessionFactory,
            SessionFactoryServiceRegistry serviceRegistry) {
        // As you might expect, an EventListenerRegistry is the thing with which event listeners are registered  It is a
        // service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );

        // If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an
        // implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this
        eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy );

        // EventListenerRegistry defines 3 ways to register listeners:
        //     1) This form overrides any existing registrations with
        eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners );
        //     2) This form adds the specified listener(s) to the beginning of the listener chain
        eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst );
        //     3) This form adds the specified listener(s) to the end of the listener chain
        eventListenerRegistry.appendListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast );
    }
}

12.10. Envers

12.10.1. Hibernate Envers

Hibernate Envers は監査およびバージョニングシステムであり、永続クラスへの変更履歴を追跡する方法を JBoss EAP に提供します。エンティティーに対する変更履歴を保存する @Audited アノテーションが付けられたエンティティーに対して監査テーブルが作成されます。その後、データの取得と問い合わせが可能になります。

Envers により開発者は次の作業を行うことが可能になります。

  • JPA 仕様によって定義されるすべてのマッピングの監査
  • JPA 仕様を拡張するすべての Hibernate マッピングの監査
  • ネイティブ Hibernate API によりマッピングされた監査エンティティー
  • リビジョンエンティティーを用いて各リビジョンのデータをログに記録
  • 履歴データのクエリー

12.10.2. 永続クラスの監査

JBoss EAP では、Hibernate Envers と @Audited アノテーションを使用して永続クラスの監査を行います。アノテーションがクラスに適用されると、エンティティーのリビジョン履歴が保存されるテーブルが作成されます。

クラスに変更が加えられるたびに監査テーブルにエントリーが追加されます。エントリーにはクラスへの変更が含まれ、リビジョン番号が付けられます。そのため、変更をロールバックしたり、以前のリビジョンを表示したりすることが可能です。

12.10.3. 監査ストラテジー

12.10.3.1. 監査ストラテジー

監査ストラテジーは、監査情報の永続化、クエリー、および格納の方法を定義します。Hibernate Envers には、現在 2 つの監査ストラテジーが存在します。

デフォルトの監査ストラテジー
  • このストラテジーは監査データと開始リビジョンを共に永続化します。監査テーブルで挿入、更新、削除された各行については、開始リビジョンの有効性と合わせて、1 つ以上の行が監査テーブルに挿入されます。
  • 監査テーブルの行は挿入後には更新されません。監査情報のクエリーはサブクエリーを使い監査テーブルの該当行を選択します (これは時間がかかり、インデックス化が困難です)。
妥当性監査ストラテジー
  • このストラテジーは監査情報の開始リビジョンと最終リビジョンの両方を格納します。監査テーブルで挿入、更新、または削除された各行については、開始リビジョンの有効性とあわせて、1 つ以上の行が監査テーブルに挿入されます。
  • 同時に、以前の監査行 (利用可能な場合) の最終リビジョンフィールドがこのリビジョンに設定されます。監査情報に対するクエリーは、サブクエリーの代わりに開始と最終リビジョンのいずれかを使用します。つまり、更新の数が増えるため監査情報の永続化には今までより少し時間がかかりますが、監査情報の取得は非常に早くなります。
  • インデックスを増やすことで改善することも可能です。

監査の詳細については、永続クラスの監査を参照してください。アプリケーションの監査ストラテジーを設定するには、 監査ストラテジーの設定を参照してください。

12.10.3.2. 監査ストラテジーの設定

JBoss EAP では、2 つの監査ストラテジーがサポートされます。

  • デフォルトの監査ストラテジー
  • 妥当性監査ストラテジー
監査ストラテジーの定義

アプリケーションの persistence.xml ファイルで org.hibernate.envers.audit_strategy プロパティーを設定します。このプロパティーが persistence.xml ファイルで設定されていない場合は、デフォルトの監査ストラテジーが使用されます。

デフォルトの監査ストラテジーの設定

<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.DefaultAuditStrategy"/>

妥当性監査ストラテジーの設定

<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.ValidityAuditStrategy"/>

12.10.4. JPA エンティティーへの監査サポートの追加

JBoss EAP は、エンティティーの監査を使用して (Hibernate Envers を参照)、永続クラスの変更履歴を追跡します。本トピックでは、JPA エンティティーに対する監査サポートを追加する方法について説明します。

JPA エンティティーへの監査サポートの追加

  1. デプロイメントに適した使用可能な監査パラメーターを設定します (Envers パラメーターの設定を参照)。
  2. 監査対象となる JPA エンティティーを開きます。
  3. org.hibernate.envers.Audited インターフェースをインポートします。
  4. 監査対象となる各フィールドまたはプロパティーに @Audited アノテーションを付けます。または、1 度にクラス全体へアノテーションを付けます。

    例: 2 つのフィールドの監査

    import org.hibernate.envers.Audited;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Column;
    
    @Entity
    public class Person {
        @Id
        @GeneratedValue
        private int id;
    
        @Audited
        private String name;
    
        private String surname;
    
        @ManyToOne
        @Audited
        private Address address;
    
        // add getters, setters, constructors, equals and hashCode here
    }

    例: クラス全体の監査

    import org.hibernate.envers.Audited;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Column;
    
    @Entity
    @Audited
    public class Person {
        @Id
        @GeneratedValue
        private int id;
    
        private String name;
    
        private String surname;
    
        @ManyToOne
        private Address address;
    
        // add getters, setters, constructors, equals and hashCode here
    }

JPA エンティティーの監査が設定されると、変更履歴を保存するために _AUD という名前のテーブルが作成されます。

12.10.5. Configuration (設定)

12.10.5.1. Envers パラメーターの設定

JBoss EAP は、Hibernate Envers からエンティティーの監査を使用して、永続クラスの変更履歴を追跡します。

利用可能な Envers パラメーターの設定

  1. アプリケーションの persistence.xml ファイルを開きます。
  2. 必要に応じて Envers プロパティーを追加、削除、または設定します。使用可能なプロパティーの一覧については、Envers の設定プロパティーを参照してください。

    例: Envers パラメーター

    <persistence-unit name="mypc">
      <description>Persistence Unit.</description>
      <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
      <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
      <properties>
        <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.cache.use_query_cache" value="true" />
        <property name="hibernate.generate_statistics" value="true" />
        <property name="org.hibernate.envers.versionsTableSuffix" value="_V" />
        <property name="org.hibernate.envers.revisionFieldName" value="ver_rev" />
      </properties>
    </persistence-unit>

12.10.5.2. ランタイム時に監査を有効または無効にする

ランタイム時にエンティティーバージョン監査を有効または無効にする

  1. AuditEventListener クラスをサブクラス化します。
  2. Hibernate イベント上で呼び出される次のメソッドを上書きします。

    • onPostInsert
    • onPostUpdate
    • onPostDelete
    • onPreUpdateCollection
    • onPreRemoveCollection
    • onPostRecreateCollection
  3. イベントのリスナーとしてサブクラスを指定します。
  4. 変更を監査すべきであるか判断します。
  5. 変更を監査する必要がある場合は、呼び出しをスーパークラスへ渡します。

12.10.5.3. 条件付き監査の設定

Hibernate Envers は一連のイベントリスナーを使用して、さまざまな Hibernate イベントに対して監査データを永続化します。Envers jar がクラスパスにある場合、これらのリスナーは自動的に登録されます。

条件付き監査の実装

  1. persistence.xml ファイルで hibernate.listeners.envers.autoRegister の Hibernate プロパティーを false に設定します。
  2. 上書きする各イベントリスナーをサブクラス化します。条件付き監査の論理をサブクラスに置き、監査の実行が必要な場合はスーパーメソッドを呼び出します。
  3. org.hibernate.envers.event.EnversIntegrator と似ている org.hibernate.integrator.spi.Integrator のカスタム実装を作成します。デフォルトのクラスではなく、手順 2 で作成したイベントリスナーサブクラスを使用します。
  4. jar に META-INF/services/org.hibernate.integrator.spi.Integrator ファイルを追加します。このファイルにはインターフェースを実装するクラスの完全修飾名を含める必要があります。

12.10.5.4. Envers の設定プロパティー

表12.24 エンティティーデータのバージョニング設定パラメーター
プロパティー名デフォルト値説明

org.hibernate.envers.audit_table_prefix

デフォルト値なし

監査エンティティーの名前の前に付けられた文字列。監査情報を保持するエンティティーの名前を作成します。

org.hibernate.envers.audit_table_suffix

_AUD

監査情報を保持するエンティティーの名前を作成する監査エンティティーの名前に追加された文字列。たとえば、Person のテーブル名を持つエンティティーが監査される場合は、Envers により履歴データを格納する Person_AUD と呼ばれるテーブルが生成されます。

org.hibernate.envers.revision_field_name

REV

改訂番号を保持する監査エンティティーのフィールド名。

org.hibernate.envers.revision_type_field_name

REVTYPE

リビジョンタイプを保持する監査エンティティーのフィールド名。挿入、変更、または削除のための現在のリビジョンタイプは、それぞれ addmod、および del です。

org.hibernate.envers.revision_on_collection_change

true

このプロパティーは、所有されていない関係フィールドが変更された場合にリビジョンを生成するかどうかを決定します。これは、一対多関係のコレクションまたは一対一関係の mappedBy 属性を使用したフィールドのいずれかです。

org.hibernate.envers.do_not_audit_optimistic_locking_field

true

true の場合、オプティミスティックロッキングに使用したプロパティー (@Version アノテーションが付いたもの) は自動的に監査から除外されます。

org.hibernate.envers.store_data_at_delete

false

このプロパティーは、ID のみではなく、他の全プロパティーが null とマークされたエンティティーが削除される場合にエンティティーデータをリビジョンに保存すべきかどうかを定義します。このデータは最終リビジョンに存在するため、これは通常必要ありません。最終リビジョンのデータにアクセスする方が簡単で効率的ですが、この場合、削除前にエンティティーに含まれたデータが 2 回保存されることになります。

org.hibernate.envers.default_schema

null (通常のテーブルと同じ)

監査テーブルに使用されるデフォルトのスキーマ名。@AuditTable(schema="…​") アノテーションを使用してオーバーライドできます。このスキーマがない場合、スキーマは通常のテーブルのスキーマと同じです。

org.hibernate.envers.default_catalog

null (通常のテーブルと同じ)

監査テーブルに使用するデフォルトのカタログ名。@AuditTable(catalog="…​") アノテーションを使用してオーバーライドできます。このカタログがない場合、カタログは通常のテーブルのカタログと同じです。

org.hibernate.envers.audit_strategy

org.hibernate.envers.strategy.DefaultAuditStrategy

このプロパティーは、監査データを永続化する際に使用する監査ストラテジーを定義します。デフォルトでは、エンティティーが変更されたリビジョンのみが保存されます。あるいは、org.hibernate.envers.strategy.ValidityAuditStrategy が、開始リビジョンと最終リビジョンの両方を保存します。これらは、監査行が有効である場合に定義されます。

org.hibernate.envers.audit_strategy_validity_end_rev_field_name

REVEND

監査エンティティーのリビジョン番号を保持するカラムの名前。このプロパティーは、妥当性監査ストラテジーが使用されている場合のみ有効です。

org.hibernate.envers.audit_strategy_validity_store_revend_timestamp

false

このプロパティーは、データが最後に有効だった最終リビジョンのタイムスタンプを最終リビジョンとともに格納するかどうかを定義します。これは、テーブルパーティショニングを使用することにより、関係データベースから以前の監査レコードを削除する場合に役に立ちます。パーティショニングには、テーブル内に存在する列が必要です。このプロパティーは、ValidityAuditStrategy が使用される場合にのみ評価されます。

org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name

REVEND_TSTMP

データが有効であった最終リビジョンのタイムスタンプの列名。ValidityAuditStrategy が使用され、org.hibernate.envers.audit_strategy_validity_store_revend_timestamp が true と評価された場合のみ使用されます。

12.10.6. クエリーを介した監査情報の読み出し

Hibernate Envers は、クエリーから監査情報を読み出しする機能を提供します。

注記

監査されたデータのクエリーは相関サブセレクトが関与するため、多くの場合で live データの対応するクエリーよりも大幅に処理が遅くなります。

特定のリビジョンでクラスのエンティティーをクエリーする

このようなクエリーのエントリーポイントは次のとおりです。

AuditQuery query = getAuditReader()
    .createQuery()
    .forEntitiesAtRevision(MyEntity.class, revisionNumber);

AuditEntity ファクトリクラスを使用して制約を指定することができます。以下のクエリーは、name プロパティーが John と同等である場合のみエンティティーを選択します。

query.add(AuditEntity.property("name").eq("John"));

以下のクエリーは特定のエンティティーと関連するエンティティーのみを選択します。

query.add(AuditEntity.property("address").eq(relatedEntityInstance));
// or
query.add(AuditEntity.relatedId("address").eq(relatedEntityId));

結果を順序付けや制限付けしたり、凝集 (aggregations) および射影 (projections) のセット (グループ化を除く) を持つことが可能です。以下はフルクエリーの例になります。

List personsAtAddress = getAuditReader().createQuery()
    .forEntitiesAtRevision(Person.class, 12)
    .addOrder(AuditEntity.property("surname").desc())
    .add(AuditEntity.relatedId("address").eq(addressId))
    .setFirstResult(4)
    .setMaxResults(2)
    .getResultList();

特定クラスのエンティティーが変更された場合のクエリーリビジョン

このようなクエリーのエントリーポイントは次のとおりです。

AuditQuery query = getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true);

前の例と同様に、このクエリーへ制約を追加することが可能です。このクエリーに以下を追加することも可能です。

AuditEntity.revisionNumber()
監査されたエンティティーが修正されたリビジョン番号の制約や射影、順序付けを指定します。
AuditEntity.revisionProperty(propertyName)
監査されたエンティティーが修正されたリビジョンに対応するリビジョンエンティティーのプロパティーの制約や射影、順序付けを指定します。
AuditEntity.revisionType()
リビジョンのタイプ (ADD、MOD、DEL) へのアクセスを提供します。

クエリー結果を必要に応じて調整することが可能です。次のクエリーは、リビジョン番号 42 の後に entityId ID を持つ MyEntity クラスのエンティティーが変更された最小のリビジョン番号を選択します。

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.id().eq(entityId))
    .add(AuditEntity.revisionNumber().gt(42))
    .getSingleResult();

リビジョンのクエリーはプロパティーを最小化および最大化することも可能です。次のクエリーは、特定エンティティーの actualDate 値が指定の値よりは大きく、可能な限り小さいリビジョンを選択します。

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    // We are only interested in the first revision
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.property("actualDate").minimize()
        .add(AuditEntity.property("actualDate").ge(givenDate))
        .add(AuditEntity.id().eq(givenEntityId)))
    .getSingleResult();

minimize() および maximize() メソッドは制約を追加できる基準を返します。最大化または最小化されたプロパティーを持つエンティティーはこの基準を満たさなければなりません。

クエリー作成時に渡されるブール変数パラメーターは 2 つあります。

selectEntitiesOnly

このパラメーターは、明示的なプロジェクションが設定されていない場合のみ有効です。
true の場合、クエリーの結果は指定された制約を満たすリビジョンで変更されたエンティティーのリストです。
false の場合、結果は 3 つの要素アレイのリストです。最初の要素は変更されたエンティティーインスタンスです。2 つ目の要素はリビジョンデータを含むエンティティーです。カスタムエンティティーが使用されない場合、これは DefaultRevisionEntity のインスタンスです。3 つ目の要素アレイはリビジョンの種類 (ADD、MOD、DEL) です。
selectDeletedEntities
このパラメーターは、エンティティーが削除されたリビジョンが結果に含まれなければならない場合に指定されます。true の場合、エンティティーのリビジョンタイプが DEL になり、id 以外のすべてのフィールドの値が null になります。

特定のプロパティーを修正したエンティティーのクエリーリビジョン

下記のクエリーは、actualDate プロパティーが変更された、指定の ID を持つ MyEntity のすべてのリビジョンを返します。

AuditQuery query = getAuditReader().createQuery()
  .forRevisionsOfEntity(MyEntity.class, false, true)
  .add(AuditEntity.id().eq(id));
  .add(AuditEntity.property("actualDate").hasChanged())

hasChanged 条件は他の基準と組み合わせることができます。次のクエリーは、revisionNumber の生成時に MyEntity の水平スライスを返します。これは、prop2 ではなく prop1 を変更したリビジョンに限定されます。

AuditQuery query = getAuditReader().createQuery()
  .forEntitiesAtRevision(MyEntity.class, revisionNumber)
  .add(AuditEntity.property("prop1").hasChanged())
  .add(AuditEntity.property("prop2").hasNotChanged());

結果セットには revisionNumber よりも小さい番号のリビジョンも含まれます。これは、このクエリーが「prop1 が変更され、prop2 が変更されない revisionNumber で変更された MyEntities をすべて返す」とは読み取られないことを意味します。

次のクエリーは forEntitiesModifiedAtRevision クエリーを使用してこの結果をどのように返すことができるかを示します。

AuditQuery query = getAuditReader().createQuery()
  .forEntitiesModifiedAtRevision(MyEntity.class, revisionNumber)
  .add(AuditEntity.property("prop1").hasChanged())
  .add(AuditEntity.property("prop2").hasNotChanged());

特定のリビジョンで修正されたクエリーエンティティー

次の例は、特定のリビジョンで変更されたエンティティーに対する基本的なクエリーになります。読み出される特定のリビジョンで、エンティティー名と対応する Java クラスを変更できます。

Set<Pair<String, Class>> modifiedEntityTypes = getAuditReader()
    .getCrossTypeRevisionChangesReader().findEntityTypes(revisionNumber);

org.hibernate.envers.CrossTypeRevisionChangesReader からもアクセスできる他のクエリーは以下のとおりです。

List<Object> findEntities(Number)
特定のリビジョンで変更 (追加、更新、削除)されたすべての監査済みエンティティーのスナップショットを返します。n+1 個の SQL クエリを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数になります)。
List<Object> findEntities(Number, RevisionType)
変更タイプによってフィルターされた特定のリビジョンで変更 (追加、更新、削除) されたすべての監査済みエンティティーのスナップショットを返します。n+1 個の SQL クエリーを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数です)。Map<RevisionType, List<Object>>
findEntitiesGroupByRevisionType(Number)
修正操作 (追加、更新、削除など) によってグループ化されたエンティティースナップショットの一覧が含まれるマップを返します。3n+1 個の SQL クエリーを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数になります)。

12.11. パフォーマンスチューニング

12.11.1. 代替のバッチローディングアルゴリズム

Hibernate では、4 つのフェッチングストラテジー (join、select、subselect、および batch) のいずれかを使用してアソシエーションのデータをロードできます。batch ローディングは select フェッチングの最適化ストラテジーであるため、パフォーマンスを最大化できます。このストラテジーでは、主キーまたは外部キーのリストを指定することで、Hibernate が単一の SELECT ステートメントでエンティティーインスタンスまたはコレクションのバッチを読み出します。batch フェッチングは、レイジー select フェッチングストラテジーの最適化です。

batch フェッチングを設定する方法には、クラスごとのレベルと、コレクションごとのレベルの 2 つの方法があります。

  • クラスごとのレベル

    Hibernate がクラスごとのレベルでデータをロードする場合、クエリー時に事前ロードするアソシエーションのバッチサイズが必要になります。たとえば、起動時に car オブジェクトの 30 個のインスタンスがセッションでロードされるとします。各 car オブジェクトは owner オブジェクトに属します。lazy ローディングで、すべての car オブジェクトを繰り返し、これらの owner (所有者) を要求する場合、Hibernate は owner ごとに 1 つ、合計 30 個の select ステートメントを発行します。これは、パフォーマンス上のボトルネックになります。

    この代わりに、クエリーによって要求される前に次の owner のバッチに対してデータを事前ロードするよう Hibernate を指示できます。owner オブジェクトがクエリーされると、Hibernate は同じ SELECT ステートメントでこれらのオブジェクトをさらに多くクエリーします。

    事前にクエリーされる owner オブジェクトの数は、設定時に指定された batch-size パラメーターによって異なります。

    <class name="owner" batch-size="10"></class>

    これは、今後必要になると見込まれる最低 10 個の owner オブジェクトをクエリーするよう Hibernate に指示します。ユーザーが car Aowner をクエリーする場合、car Bowner はすでにバッチローディングの一部としてロードされていることがあります。ユーザーが実際に car Bowner を必要とする場合、データーベースにアクセスして SELECT ステートメントを発行する代わりに、現在のセッションから値を読み出すことができます。

    Hibernate 4.2.0 には、batch-size パラメーターの他に、バッチローディングのパフォーマンスを向上する新しい設定項目が追加されました。この設定項目は Batch Fetch Style 設定と呼ばれ、hibernate.batch_fetch_style パラメーターによって指定されます。

    LEGACY、PADDED、および DYNAMIC の 3 つのバッチフェッチスタイルがサポートされます。使用するスタイルを指定するには、org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE を使用します。

    • LEGACY: LEGACY スタイルのローディングでは、ArrayHelper.getBatchSizes(int) に基づいた一連の事前ビルド済みバッチサイズが使用されます。バッチは、既存のバッチ可能な識別子の数から、次に小さい事前ビルド済みバッチサイズを使用してロードされます。

      前述の例を用いた場合、batch-size の設定が 30 であると、事前ビルドされたバッチサイズは [30, 15, 10, 9, 8, 7, .., 1] になります。29 個の識別子をバッチロードしようとすると、バッチは 15、10、および 4 になります。対応する 3 つの SQL クエリーが生成され、各クエリーはデータベースより 15、10、および 4 人の owner をロードします。

    • PADDED - PADDED は LEGACY スタイルのバッチローディングと似ています。PADDED も事前ビルドされたバッチサイズを使用しますが、次に大きなバッチサイズを使用し、余分な識別子プレースホルダーを埋め込みます。

      上記の例で、30 個の owner オブジェクトが初期化される場合は、データベースに対して 1 つのクエリーのみが実行されます。

      29 個の owner オブジェクトが初期化される場合、Hibernate は同様にバッチサイズが 30 の SQL select ステートメントを 1 つ実行しますが、余分なスペースが繰り替えされる識別子で埋め込みされます。

    • DYNAMIC - DYNAMIC スタイルのバッチローディングはバッチサイズの制限に準拠しますが、実際にロードされるオブジェクトの数を使用して SQL SELECT ステートメントを動的にビルドします。

      たとえば、30 個の owner オブジェクトで最大バッチサイズが 30 の場合、 30 個の owner オブジェクトの読み出しは 1 つの SQL SELECT ステートメントによって実行されます。35 個の owner オブジェクトを読み出す場合は、SQL ステートメントが 2 つになり、それぞれのバッチサイズが 30 と 5 になります。Hibernate は、制限どおりにバッチサイズを 30 以下とし、2 つ目の SQL ステートメントを動的に変更して、必要数である 5 にします。PADDED とは異なり、2 つ目の SQL は埋め込みされません。また、2 つ目の SQL ステートメントは動的に作成され、固定サイズでないことが LEGACY とは異なります。

      30 個未満のクエリーでは、このスタイルは要求された識別子の数のみを動的にロードします。

  • コレクションごとのレベル

    Hibernate は、前項の「クラスごとのレベル」で説明したバッチフェッチサイズとスタイルを維持してコレクションをバッチロードすることもできます。

    前項の例を逆にして、各 owner オブジェクトによって所有されるすべての car オブジェクトをロードする必要があるとします。10 個の owner オブジェクトがすべての owner を繰り返し、現セッションにロードされた場合、getCars() メソッドの呼び出しごとに 1 つの SELECT ステートメントが生成されるため、合計で 10 個の SELECT ステートメントが生成されます。owner のマッピングでの car コレクションのバッチフェッチングを有効にすると、Hibernate は下記のようにこれらのコレクションを事前フェッチできます。

    <class name="Owner"><set name="cars" batch-size="5"></set></class>

    よって、バッチサイズが 5 でレガシーバッチスタイルを使用して 10 個のコレクションをロードする場合、Hibernate は 2 つの SELECT ステートメントを実行し、各ステートメントは 5 つのコレクションを読み出します。

12.11.2. 不変データのオブジェクト参照の 2 次キャッシング

Hibernate はパフォーマンスを向上するため、自動的にデータをメモリー内にキャッシュします。これは、データベースのルックアップが必要となる回数を削減する (特にほとんど変更されないデータに対し) インメモリーキャッシュによって実現されます。

Hibernate は 2 つのタイプのキャッシュを保持します。1 次キャッシュ (プライマリーキャッシュ) は必須のキャッシュです。このキャッシュは現在のセッションと関連し、すべてのリクエストが通過する必要があります。2 次キャッシュ (セカンダリーキャッシュ) は任意のキャッシュで、1 次キャッシュがアクセスされた後でのみアクセスされます。

データは、最初にステートアレイに逆アセンブルされ、2 次キャッシュに保存されます。このアレイはディープコピーされ、ディープコピーがキャッシュに格納されます。キャッシュからデータを読み取る場合は、この逆のプロセスが発生します。この仕組みは、変化するデータ (可変データ) ではうまく機能しますが、不変データでは不十分です。

データのディープコピーは、メモリーの使用と処理速度に負荷のかかる操作です。大きなデータセットでは、メモリーおよび処理速度がパフォーマンスを制限する要素になります。Hibernate では、不変データがコピーされずに参照されるよう指定できます。Hibernate はデータセット全体をコピーする代わりに、データへの参照をキャッシュに保存できます。

これは、設定 hibernate.cache.use_reference_entries の値を true に変更することによって行えます。デフォルトでは、hibernate.cache.use_reference_entriesfalse に設定されます。

hibernate.cache.use_reference_entriestrue に設定されると、アソシエーションを持たない不変データオブジェクトは 2 次キャッシュにコピーされず、不変データオブジェクトへの参照のみが保存されます。

警告

hibernate.cache.use_reference_entriestrue に設定されても、アソシエーションを持つ不変データオブジェクトは 2 次キャッシュにディープコピーされます。

Red Hat logoGithubRedditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

© 2024 Red Hat, Inc.