Hibernateは、Java環境のためのオブジェクト・リレーショナル・マッピング・ツールです。Oracle CoherenceとHibernateの機能は様々な方法で連携できます。
Coherenceには、デフォルトのエンティティベースのCacheStore実装として、com.tangosol.coherence.hibernateパッケージ内にあるHibernateCacheStore(および対応するCacheLoader実装であるHibernateCacheLoader)が組み込まれています。
HibernateCacheStoreおよびHibernateCacheLoader APIには、次のコンストラクタが用意されています。
HibernateCacheLoader()、HibernateCacheStore(): CacheLoaderまたはCacheStoreの新しいインスタンスを作成するデフォルト・コンストラクタです。これらのコンストラクタは、SessionFactoryを作成しません。SessionFactoryを作成するには、setSession()メソッドを使用します。
HibernateCacheLoader(java.lang.String entityName)、HibernateCacheStore(java.lang.String entityName): デフォルトのHibernateコンフィギュレーション(hibernate.cfg.xml)をクラスパス内で使用しSessionFactoryを作成します。
HibernateCacheStore(java.lang.String entityName, java.lang.String sResource)、HibernateCacheStore(java.lang.String entityName, java.lang.String sResource): 提供されるコンフィギュレーション・ファイル(sResource)に基づいてSessionFactoryを作成します。
HibernateCacheLoader(java.lang.String entityName, java.io.File configurationFile), HibernateCacheStore(java.lang.String entityName, java.io.File configurationFile): 提供されるコンフィギュレーション・ファイル(configurationFile)に基づいてSessionFactoryを作成します。
HibernateCacheStore(java.lang.String entityName, org.hibernate.SessionFactory sFactory)、HibernateCacheStore(java.lang.String entityName, org.hibernate.SessionFactory sFactory): entityNameおよびHibernateセッション・ファクトリを受け取るキャッシュ・ストア・コンストラクタです。
詳細な技術情報については、実装するクラスのJavadocを参照してください。
次の例は、エンティティ名のみを受け入れる単純なHibernateCacheStoreコンストラクタを示しています。これは、デフォルトのコンフィギュレーション・パスを使用してHibernateを構成し、クラスパス内でhibernate.cfg.xmlファイルを探します。また、hibernate.cfg.xmlファイルのリソース名またはファイル仕様を第2の<init-param>として渡す機能もあります(リソース名の場合は<param-type>要素をjava.lang.Stringに設定し、ファイル仕様の場合はjava.io.Fileに設定する)。詳細は、「HibernateCacheStore」を参照してください。
例3-1は、Hibernateエンティティ(com.company.TableA)のインスタンスをキャッシュする、TableAという名前のNamedCacheの定義に使用されるcoherence-cache-config.xmlファイルのサンプルを示しています。エンティティ・キャッシュを追加するには、<cache-mapping>要素を追加します。
例3-1 Hibernate用のcoherence-cache-config.xmlファイルのサンプル
<?xml version="1.0"?> <!DOCTYPE cache-config SYSTEM "cache-config.dtd"> <cache-config> <caching-scheme-mapping> <cache-mapping> <cache-name>TableA</cache-name> <scheme-name>distributed-hibernate</scheme-name> <init-params> <init-param> <param-name>entityname</param-name> <param-value>com.company.TableA</param-value> </init-param> </init-params> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <distributed-scheme> <scheme-name>distributed-hibernate</scheme-name> <backing-map-scheme> <read-write-backing-map-scheme> <internal-cache-scheme> <local-scheme></local-scheme> </internal-cache-scheme> <cachestore-scheme> <class-scheme> <class-name> com.tangosol.coherence.hibernate.HibernateCacheStore </class-name> <init-params> <init-param> <param-type>java.lang.String</param-type> <param-value>{entityname}</param-value> </init-param> </init-params> </class-scheme> </cachestore-scheme> </read-write-backing-map-scheme> </backing-map-scheme> </distributed-scheme> </caching-schemes> </cache-config>
例3-2では、事前定義の{cache-name}マクロを使用して、キャッシュ・マッピングのinit-params部分を不要にできることも示しています。
例3-2 {cache-name}マクロを使用したcoherence-cache-config.xmlファイルのサンプル
<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>TableA</cache-name>
<scheme-name>distributed-hibernate</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>distributed-hibernate</scheme-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<internal-cache-scheme>
<local-scheme></local-scheme>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>
com.tangosol.coherence.hibernate.HibernateCacheStore
</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>com.company.{cache-name}</param-value>
</init-param>
</init-params>
</class-scheme>
</cachestore-scheme>
</read-write-backing-map-scheme>
</backing-map-scheme>
</distributed-scheme>
</caching-schemes>
</cache-config>
例3-3では、ネーミング規則で許可される場合は、マッピングを完全に汎用化して、任意の修飾されたクラス名(エンティティ名)のキャッシュ・マッピングを使用可能にできることを示しています。
例3-3 汎用マッピングを指定したcoherence-cache-config.xmlファイルのサンプル
<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>com.company.*</cache-name>
<scheme-name>distributed-hibernate</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>distributed-hibernate</scheme-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<internal-cache-scheme>
<local-scheme></local-scheme>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>
com.tangosol.coherence.hibernate.HibernateCacheStore
</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>{cache-name}</param-value>
</init-param>
</init-params>
</class-scheme>
</cachestore-scheme>
</read-write-backing-map-scheme>
</backing-map-scheme>
</distributed-scheme>
</caching-schemes>
</cache-config>
HibernateCacheStoreモジュールを使用してアクセスされるHibernateエンティティでは、割り当てられたIDジェネレータを使用する必要があり、定義済のIDプロパティも必要です。
HibernateCacheStoreで使用されるhibernate.cfg.xmlのhibernate.hbm2ddl.autoプロパティは、無効にしてください。そうしない場合、過剰なスキーマ更新(および検索)が発生する可能性があるためです。
データベースへのすべてのアクセスがCoherenceを介して実行される場合、(パーティション・キャッシュ・サービスを使用して)読取りと書込みがキーごとにシリアルで実行されるため、CacheStoreモジュールによって必然的にANSIスタイルの反復可能読取りの分離が強制されます。CacheStoreの操作は複数のパーティション・キャッシュ・ノード(つまり、複数のデータベース・トランザクション)にまたがる場合があるため、反復可能読取りを超えるデータベース分離を設定しても、さらに分離されるわけではありません。反復可能読取りよりも少ないデータベース分離レベルを使用すると、予期しない異常を発生させずに、データベース・サーバー上の処理負荷が減少する場合があります。
single-cache-entry更新の場合、サーバー障害時(部分更新時の障害を含む)のキャッシュとデータベースの整合性が保証されるため、CacheStore操作は完全にフォルト・トレラントになります。フォルト・トレランスには様々なメカニズムがありますが、これはライトスルーとライトビハインドの両方のキャッシュで当てはまります。
Coherenceは、複数のCacheStoreインスタンスにまたがる、2フェーズのCacheStore操作をサポートしていません。つまり、2つのキャッシュ・エントリが更新された後で、別のサーバー上にあるCacheStoreモジュールへのコールがトリガーされる場合、一方のデータベースでは更新が成功しますが、もう一方では失敗する可能性があります。この場合、アプリケーション・サーバーのトランザクション・マネージャで、キャッシュアサイド・アーキテクチャ(キャッシュとデータベースを、単一トランザクション内の2つの別個のコンポーネントとして更新する)を使用することをお薦めします。ほとんどの場合、論理コミットの失敗がないように(当然、サーバー障害が発生しないようにもする)データベース・スキーマを設計することは可能です。ライトビハインド・キャッシングでは、putがデータベース動作の影響を受けないため、この問題は回避されます(根本的な問題は、設計プロセスの初期段階で解決しておく必要がある)。
アプリケーション固有の機能でHibernateCacheStoreを拡張することが必要な状況もあります。最も明白な事例として、既存のプログラムで構成されたSessionFactoryインスタンスを利用する場合があります。
提供されるHibernateCacheStoreモジュールには、ほとんどのエンティティベース・キャッシュに対するソリューションが用意されていますが、アプリケーション固有のCacheStoreモジュールが必要な場合もあります。たとえば、パラメータ化された問合せの使用や、問合せ結果のインクルードまたは後処理などがあげられます。
CacheStoreでバッキングされたキャッシュ実装では、アプリケーション・スレッドがキャッシュ・データにアクセスすると、キャッシュ操作が管理対象のCacheServiceを使用して、関連するCacheStore実装に対するコールをトリガーする場合があります。CacheStoreは、CacheService APIに対するコール・バックを実行できません。これは間接的には、Hibernateがキャッシュ・データへのアクセスを試行できないことを意味します。そのため、CacheLoaderまたはCacheStoreのすべてのメソッドには、Session.setCacheMode(CacheMode.IGNORE)に対するコールを設定して、キャッシュ・アクセスを無効にしておく必要があります。または、Hibernateコンフィギュレーションで、キャッシュが無効化されたCacheStore実装のバージョンを使用してクローニングする必要があります(プログラムで実行するか、hibernate.cfg.xmlを使用する)。
完全にキャッシュされたデータセットの使用を有用にするには、2つのシナリオがあります。1つは、キャッシュで分散問合せを実行する場合で、もう1つは、データベースの障害時でもアプリケーションの処理を継続させる場合です。
分散問合せは、データベース・サーバーでの問合せの実行と比べて、待機時間の短縮、スループットの向上、データベース・サーバー負荷の低減を実現する点で有利な場合があります。セット指向の問合せの場合、正しい問合せ結果を生成するには、データセット全体をキャッシュする必要があります。キャッシュに対する問合せで正しい結果を得るには、その問合せはキャッシュされていないデータに依存できません。
分散問合せによりハイブリッド・キャッシュを作成できます。たとえば、問合せ用の完全にキャッシュされたサイズ制限のあるデータセット(先週のデータなど)と、シングルトンの読取りに使用される部分的にキャッシュされた履歴データセットなど、2つの用途のNamedCacheを組み合せることができます。これは、データの複製を回避し、メモリー使用量を抑えるための優れたアプローチです。
完全にキャッシュされたデータセットは通常、アプリケーションの起動時に(または定期的に)バルクロードされますが、CacheStore統合を使用して、キャッシュとデータベースを両方とも完全に同期化させることもできます。
Coherenceは、HibernateのL2キャッシュ・プロバイダとして使用できます。
Hibernateは、次の3つの主要なキャッシュ形式をサポートしています。
セッション・キャッシュ
L2キャッシュ
問合せキャッシュ
Sessionキャッシュは、Session(Hibernateトランザクション、複数のデータベース・トランザクションにまたがる場合もあり、通常はスレッドごとに範囲指定される)内のレコードのキャッシングを行います。Sessionキャッシュは、非クラスタ・キャッシュ(定義による)として、Hibernateによって全面的に管理されます。L2および問合せキャッシュは、複数のトランザクションにまたがる、Coherenceキャッシュ・プロバイダとしての使用をサポートします。L2キャッシュは、複数のセッションにわたるレコードのキャッシュを(主キーの検索用に)行います。問合せキャッシュは、Hibernate問合せによって生成された結果セットをキャッシュします。Hibernateでは、L2および問合せキャッシュにおいてデータを内部形式で管理するため、これらのキャッシュはHibernateでのみ使用可能になります。詳細は、『Hibernate Reference Documentation』(Hibernateに付属)の、2次レベル・キャッシュに関する項を参照してください。
HibernateでCoherenceキャッシュ・プロバイダを使用するには、hibernate.cache.provider_classプロパティでCoherenceプロバイダ・クラスを指定します。通常、これはデフォルトのHibernateコンフィギュレーション・ファイル、hibernate.cfg.xmlで構成されます。例3-4は、CoherenceCacheProviderをhibernate.cache.provider_classとしてコールするproperty要素を示しています。
例3-4 Coherenceプロバイダ・クラスの指定
<property name="hibernate.cache.provider_class">com.tangosol.coherence.hibernate.CoherenceCacheProvider</property>
coherence-hibernate.jarファイル(lib/サブディレクトリ内にある)をアプリケーションのクラスパスに追加する必要があります。
Hibernateでは、コンフィギュレーション・プロパティhibernate.cache.use_minimal_putsでキャッシュの読取りを増加およびキャッシュの更新を減少させることによって、クラスタ・キャッシュのキャッシュ・アクセスが最適化されます。これは、Coherenceキャッシュ・プロバイダによってデフォルトで有効になっています。このプロパティをfalseに設定すると、キャッシュ管理のオーバーヘッドの増加およびトランザクション・ロールバック数の増加を招く場合があります。
Coherenceキャッシュ・プロバイダには、タイムアウトになるまでのロック獲得の試行時間を設定できます。これは、Javaプロパティtangosol.coherence.hibernate.lockattemptmillisで指定できます。デフォルトは1分です。
デフォルトでは、Coherenceキャッシュ・プロバイダは、coherence-hibernate.jarにあるconfig/hibernate-cache-config.xmlという名前のカスタム・キャッシュ・コンフィギュレーションを使用します。このコンフィギュレーション・ファイルは、Hibernate L2キャッシュのキャッシュ・マッピングを定義するために使用します。必要に応じて、tangosol.coherence.hibernate.cacheconfigのJavaプロパティを使用して、Hibernate L2キャッシュに対して代替のキャッシュ・コンフィギュレーション・リソースを指定できます。マッピングが正常に構成されている場合、このプロパティをアプリケーションのメインcoherence-cache-config.xmlファイルをポイントするように構成できます。Hibernate固有キャッシュの管理に専用のキャッシュ・サービスを使用して、CacheStoreモジュールで、Coherence管理のHibernate L2キャッシュに対するリエントラント・コールが発生しないようにすると有益な場合があります。
Coherenceキャッシュ・コンフィギュレーション・ファイルのスキーム・マッピング・セクションでは、hibernate.cache.region_prefixプロパティを使用してキャッシュ・トポロジを指定できます。たとえば、キャッシュ・コンフィギュレーション・ファイルにnear-*のようなワイルドカード・マッピングがあり、Hibernateリージョンの接頭辞プロパティがnear-に設定されている場合、すべてのHibernateキャッシュがnear-接頭辞を使用して名前付けされ、near-*キャッシュ名パターンで指定したキャッシュ・スキーム・マッピングが使用されます。
接頭辞と修飾エンティティ名の組合せ(例: near-com.company.EntityName)からキャッシュ・マッピングを作成することによって、または、空白の接頭辞を設定し、修飾エンティティ名のそれぞれにキャッシュ・マッピングを指定することによって、エンティティ別にキャッシュ・トポロジを指定できます。
また、L2キャッシュは、メモリー使用量が過剰にならないように、サイズを制限する必要があります。Hibernate APIには、完全なエビクション以外に問合せキャッシュを制御する手段がないため、問合せキャッシュでは特にサイズ制限が必要です。
Hibernateでは、通常、キャッシュとデータベースの両方でオプティミスティックな並行性の使用が重視されます。オプティミスティックな並行性では、トランザクションの開始時にアプリケーションで正確なデータを利用できるかどうかが、トランザクション処理で特に重要になります。データが不正確な場合、コミット処理の際にトランザクションが不正確なデータに基づいていることが検出され、トランザクションのコミットが失敗します。オプティミスティックなトランザクションのほとんどは、基礎となるデータへの他のプロセスによる変更に対処しますが、キャッシングを使用すると、キャッシュ自体が古くなる可能性が高くなります。Hibernateには、L2キャッシュに対する更新を制御する複数の並行性方針が用意されています。Coherenceでは、クラスタ全体でのキャッシュの一貫性をサポートするため、さほどの問題は生じませんが、キャッシュの並行性方針を適切に選択することで、アプリケーション効率の向上につながります。
キャッシュ・コンフィギュレーション方針は、表レベルで指定できます。一般に、この方針はクラスのマッピング・ファイルで指定する必要があります。
読取りと書込みが行われるアクティビティでは、read-write方針をお薦めします。トランザクション方針は、nonstrict-read-write方針と同様に実装され、Hibernateのオプティミスティックな並行性機能に依存しています。オプティミスティックな並行性に対する影響が許容できる場合は、nonstrict-read-writeのほうがパフォーマンスが高くなることがあります。
読取り専用キャッシングでは、基礎となるデータベースのデータに変更が発生しても若干古いデータは許容できる場合に、nonstrict-read-write方針を使用します。基礎となるデータベースのデータに変更が発生しない場合は、read-only方針を使用します。
問合せの結果をキャッシュするには、hibernate.cache.use_query_cacheプロパティをtrueに設定します。これで、キャッシュ可能な問合せを発行するたびに、Query.setCacheable(true)を使用して問合せ結果のキャッシュを有効にできます。Hibernateのorg.hibernate.cache.QueryKeyインスタンスは、バイナリで比較できないため(順序付けされていないデータ・メンバーの非deterministicシリアライズが実行されているため)、問合せ結果の格納にはサイズ制限のあるローカル・キャッシュまたはレプリケーション・キャッシュを使用します(これにより、キーの比較にhashcode()またはequals()メソッドが強制的に使用される)。デフォルトの問合せキャッシュ名は、org.hibernate.cache.StandardQueryCacheです(デフォルトのリージョン接頭辞が指定されていない場合。この場合、キャッシュ名の前に[prefix].が付加される)。キャッシュ・コンフィギュレーション・ファイルを使用して、このキャッシュ名をローカルまたはレプリケーション・トポロジにマップするか、問合せ時に適切にマップされたリージョン名を明示的に指定します。
Hibernate L2キャッシュ・プロトコルでは、クライアントまたはサーバーの障害発生時に完全なフォルト・トレランスがサポートされます。read-writeのキャッシュ並行性方針では、Hibernateによって更新トランザクションの開始時にキャッシュからアイテムがロックアウトされるため、クライアント側の障害では、キャッシュされないエンティティとコミットされていないトランザクションが発生するだけです。サーバー側の障害は、Coherenceによって透過的に処理されます(指定したデータ・バックアップ・カウントによって異なる)。