第23章 クラスター化した Entity EJB

JBoss Enterprise Application Platform クラスターでは、 エンティティ Bean のインスタンスは全ノードで同期される必要があります。 エンティティ Bean がリモートサービスを提供する場合、 そのサービスメソッドも負荷分散される必要があります。

23.1. EJB 3.0 内の エンティティ Bean

EJB 3.0 では、 エンティティ Bean は主に永続データモデルとして機能し、 リモートサービスを提供しません。 そのため、 EJB 3.0 内のエンティティBean クラスター化サービスは負荷分散ではなく、 主に分散キャッシングやレプリケーションに対応します。

23.1.1. 分散型キャッシュの設定

データベースへのラウンドトリップを避ける為に、ご使用のエンティティ用のキャッシュを使用することができます。JBoss EJB 3.0 エンティティ bean は、2 次キャッシュをサポートする Hibernate によって実装されます。 2 次キャッシュは以下のような機能を提供します。
  • キャッシュが有効になっている エンティティ Bean インスタンスをエンティティマネージャーを用いてデータベースへ永続化させると、 エンティティはキャッシュの中に挿入されます。
  • エンティティ Bean インスタンスを更新し、その変更をエンティティマネージャーよりデータベースに保存すると、エンティティはキャッシュ内で更新されます。
  • エンティティ Bean インスタンスをエンティティマネージャーを用いてデータベースから削除すると、 そのエンティティはキャッシュから削除されます。
  • エンティティマネージャーを介してデータベースからキャッシュ化したエンティティを ロードする時、そのエンティティがデータベース内にまだ存在しない場合、そのエンティティは キャッシュに挿入されます。
キャッシングエンティティのリージョンと同様に、 2 次キャッシュにもキャッシングコレクション、 クエリ、 タイムスタンプ向けのリージョンが含まれています。 JBoss EJB 3.0 の実装に使用される Hibernate 設定は JBoss Cache を基礎となる 2 次キャッシュ実装として使用します。
2 次キャッシュは、 EJB3 デプロイメントの persistence.xml より設定されます。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="tempdb" transaction-type="JTA">
    <jta-data-source>java:/DefaultDS</jta-data-source>
    <properties>
      <property name="hibernate.cache.use_second_level_cache" value="true"/>
      <property name="hibernate.cache.use_query_cache" value="true"/>
      <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
      <!-- region factory specific properties -->
      <property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/>
      <property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
      <property name="hibernate.cache.region.jbc2.cfg.collection" value="mvcc-entity"/>
    </properties>
  </persistence-unit>
</persistence>
hibernate.cache.use_second_level_cache
エンティティとコレクションの 2 次キャッシングを有効にします。
hibernate.cache.use_query_cache
クエリの 2 次キャッシングを有効にします。
hibernate.cache.region.factory_class
リージョン固有のキャッシング動作を指示する RegionFactory 実装を定義します。 Hibernate には、共有と多重の 2 種類の JBoss Cache ベース 2 次キャッシュが同梱されています。
共有リージョンファクトリは全てのキャッシュリージョンに同じキャッシュを使用します。 これは、 以前の Hibernate バージョンのレガシー CacheProvider 実装の動作と同様です。
Hibernate には 2 つの共有リージョンファクトリ実装が同梱されています。
org.hibernate.cache.jbc2.SharedJBossCacheRegionFactory
全てのキャッシュリージョンに対し、 新たにインスタンス化された CacheManager より単一の JBoss Cache 設定を使用します。

表23.1 SharedJBossCacheRegionFactory のその他のプロパティ

プロパティ デフォルト 詳細
hibernate.cache.region.jbc2.cfg.shared treecache.xml JBoss Cache 設定が含まれるクラスパスまたはファイルシステムリソースです。
hibernate.cache.region.jbc2.cfg.jgroups.stacks org/hibernate/cache/jbc2/builder/jgroups-stacks.xml JGroups プロトコルスタック設定が含まれるクラスパスまたはファイルシステムリソースです。
org.hibernate.cache.jbc2.JndiSharedJBossCacheRegionFactory
全てのキャッシュリージョンに対し、 既存の JNDI へバインドされた既存の CacheManager より単一の JBoss Cache 設定を使用します。

表23.2 JndiSharedJBossCacheRegionFactory のその他のプロパティ

プロパティ デフォルト 詳細
hibernate.cache.region.jbc2.cfg.shared Required 共有 Cache インスタンスがバインドされる JNDI 名です。
多重リージョンファクトリは、 各キャッシュリージョンに対して最適化された設定を使用し、 個別のキャッシュインスタンスを使用します。

表23.3 多重リージョンファクトリ実装の一般的なプロパティ

プロパティ デフォルト 詳細
hibernate.cache.region.jbc2.cfg.entity optimistic-entity エンティティキャッシュリージョンに使用される JBoss Cache 設定です。 この他に、 mvcc-entity、 pessimistic-entity、 mvcc-entity-repeatable、 optimistic-entity-repeatable、p essimistic-entity-repeatable を設定することができます。
hibernate.cache.region.jbc2.cfg.collection optimistic-entity コレクションキャッシュリージョンに使用される JBoss Cache 設定です。 通常、 コレクションキャッシュリージョンはエンティティキャッシュリージョンと同じ設定を使用します。
hibernate.cache.region.jbc2.cfg.query local-query クエリキャッシュリージョンに使用される JBoss Cache 設定です。 デフォルトでは、 キャッシュされたクエリ結果はレプリケートされません。 この他に、 replicated-query を設定することができます。
hibernate.cache.region.jbc2.cfg.ts timestamps-cache タイムスタンプキャッシュリージョンに使用される JBoss Cache 設定です。 クエリキャッシングが使用される場合、 クエリキャッシュがレプリケートしなくても、 対応するタイムスタンプキャッシュはレプリケートしなければなりません。 タイムスタンプキャッシュリージョンはクエリキャッシュと同じキャッシュを共有してはいけません。
Hibernate には 2 つの共有リージョンファクトリ実装が同梱されています。
org.hibernate.cache.jbc2.MultiplexedJBossCacheRegionFactory
キャッシュリージョン毎に、 新たにインスタンス化された CacheManager より個別の JBoss Cache 設定を使用します。

表23.4 MultiplexedJBossCacheRegionFactory のその他のプロパティ

プロパティ デフォルト 詳細
hibernate.cache.region.jbc2.configs org/hibernate/cache/jbc2/builder/jbc2-configs.xml JBoss Cache 設定が含まれるクラスパスまたはファイルシステムリソースです。
hibernate.cache.region.jbc2.cfg.jgroups.stacks org/hibernate/cache/jbc2/builder/jgroups-stacks.xml JGroups プロトコルスタック設定が含まれるクラスパスまたはファイルシステムリソースです。
org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory
キャッシュリージョン毎に、 JNDI にバインドされた CacheManager より個別の JBoss Cache 設定を使用します。 「JBoss Enterprise Application Platform の CacheManager サービス」 を参照してください。

表23.5 JndiMultiplexedJBossCacheRegionFactory のその他のプロパティ

プロパティ デフォルト 詳細
hibernate.cache.region.jbc2.cachefactory Required CacheManager インスタンスがバインドされる JNDI 名です。
これで、分散型キャッシングの EJB 3.0 エンティティ bean をサポートするための JBoss Cache を 設定したことになります。キャッシュサービスを使用するには、まだ個別の エンティティ bean を 設定する必要があります。

23.1.2. キャッシュ用の エンティティ bean の設定

次に、 キャッシュするエンティティを設定する必要があります。 上述の設定であっても、 デフォルトでは何もキャッシュしないことになっています。 キャッシュされる必要がある エンティティ bean にタグ付けするため @org.hibernate.annotations.Cache アノテーションを使用します。
@Entity 
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) 
public class Account implements Serializable
{
   // ... ... 
}
大体の目安として、 あまり変更がなく頻繁に読み込まれるオブジェクトをキャッシュします。 適切な JBoss Cache 設定ファイル jboss-cache-manager-jboss-beans.xml など) にある各エンティティ Bean に対してキャッシュを細かく調整することができます。 例えば、 キャッシュのサイズを指定することができます。 キャッシュ内にあるオブジェクトが多すぎる場合、 キャッシュは設定通りに最も古いオブジェクトや使用頻度が最も低いオブジェクトをエビクトして新しいオブジェクトのスペースを確保することができます。 例えば、 persistence.xml で指定された region_prefix が myprefix である場合、 com.mycompany.entities.Account エンティティ bean のキャッシュリージョンのデフォルト名は /myprefix/com/mycompany/entities/Account になります。
<bean name="..." class="org.jboss.cache.config.Configuration">
   ... ...
  <property name="evictionConfig">
    <bean class="org.jboss.cache.config.EvictionConfig">
      <property name="wakeupInterval">5000</property>
      <!--  Overall default -->
      <property name="defaultEvictionRegionConfig">
        <bean class="org.jboss.cache.config.EvictionRegionConfig">
          <property name="regionName">/</property>
          <property name="evictionAlgorithmConfig">
            <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
              <!-- Evict LRU node once we have more than this number of nodes -->
              <property name="maxNodes">10000</property>
              <!-- And, evict any node that hasn't been accessed in this many seconds -->
              <property name="timeToLiveSeconds">1000</property>
              <!-- Don't evict a node that's been accessed within this many seconds. 
                  Set this to a value greater than your max expected transaction length. -->
              <property name="minTimeToLiveSeconds">120</property>
            </bean>
          </property>
        </bean>
      </property>
      <property name="evictionRegionConfigs">
        <list>
          <bean class="org.jboss.cache.config.EvictionRegionConfig">
            <property name="regionName">/myprefix/com/mycompany/entities/Account</property>
            <property name="evictionAlgorithmConfig">
              <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                <property name="maxNodes">10000</property>
                <property name="timeToLiveSeconds">5000</property>
                <property name="minTimeToLiveSeconds">120</property>
              </bean>
            </property>
          </bean>
           ... ...
        </list>
      </property>
    </bean>
  </property>
</bean>
エンティティ Bean クラスに対してキャッシュリージョンを指定しないと、 上記の通り、 defaultEvictionRegionConfig を使用してそのクラスのすべてのインスタンスがキャッシュされます。 エンティティクラスの完全修飾名より自動的に作成するのではなく、 @Cache アノテーションは、 エンティティが保存されるキャッシュリージョンを指定できるようにする任意の属性「region」を公開します。
@Entity 
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account") 
public class Account implements Serializable
{ 
  // ... ... 
}
除外設定は以下のようになります。
<bean name="..." class="org.jboss.cache.config.Configuration">
   ... ...
  <property name="evictionConfig">
    <bean class="org.jboss.cache.config.EvictionConfig">
      <property name="wakeupInterval">5000</property>
      <!--  Overall default -->
      <property name="defaultEvictionRegionConfig">
        <bean class="org.jboss.cache.config.EvictionRegionConfig">
          <property name="regionName">/</property>
          <property name="evictionAlgorithmConfig">
            <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
              <property name="maxNodes">5000</property>
              <property name="timeToLiveSeconds">1000</property>
              <property name="minTimeToLiveSeconds">120</property>
            </bean>
          </property>
        </bean>
      </property>
      <property name="evictionRegionConfigs">
        <list>
          <bean class="org.jboss.cache.config.EvictionRegionConfig">
            <property name="regionName">/myprefix/Account</property>
            <property name="evictionAlgorithmConfig">
              <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                <property name="maxNodes">10000</property>
                <property name="timeToLiveSeconds">5000</property>
                <property name="minTimeToLiveSeconds">120</property>
              </bean>
            </property>
          </bean>
           ... ...
        </list>
      </property>
    </bean>
  </property>
</bean>

23.1.3. クエリ結果のキャッシュ

EJB3 のクエリ API を使用すると、指定されたクエリの結果 (エンティティ bean の 1 次キーの集まりやスカラー値の集まりなど) を 2 次キャッシュに保存できます。 ここでは、bean に名前付きクエリのアノテーションを付ける簡単な例を説明し、Hibernate でクエリをキャッシュするための Hibernate 固有のヒントも提供します。
最初に persistence.xml で Hibernate がクエリキャッシュを有効にするよう指定する必要があります。
<property name="hibernate.cache.use_query_cache" value="true"/>
次に、エンティティに関連付けられた名前付きクエリを作成し、そのクエリの結果をキャッシュすることを Hibernate に指示します。
 
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
  @NamedQuery(
    name = "account.bybranch",
    query = "select acct from Account as acct where acct.branch = ?1",
    hints = { @QueryHint(name = "org.hibernate.cacheable", value = "true") }
  )
})
public class Account implements Serializable
{
  // ... ... 
}
@NamedQueries、 @NamedQuery、 および @QueryHint アノテーションはすべて javax.persistence パッケージに含まれます。 EJB3 クエリの使用方法とクエリをキャッシュするよう EJB3 に指示する方法の詳細については、 Hibernate と EJB3 のドキュメントを参照してください。
デフォルトでは、 Hibernate は JBoss Cache のクエリ結果を <region_prefix>/org/hibernate/cache/StandardQueryCache という名前の領域に保存します。 これに基づき、 クエリ結果に対して異なるエビクション処理を設定できます。 persistence.xml でリージョンのプレフィックスが myprefix に設定されている場合は、 以下のようなエビクション処理を作成できます。
<bean name="..." class="org.jboss.cache.config.Configuration">
    ... ...
   <property name="evictionConfig">
       <bean class="org.jboss.cache.config.EvictionConfig">
         <property name="wakeupInterval">5000</property>
         <!--  Overall default -->
         <property name="defaultEvictionRegionConfig">
            <bean class="org.jboss.cache.config.EvictionRegionConfig">
               <property name="regionName">/</property>
               <property name="evictionAlgorithmConfig">
                  <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                     <property name="maxNodes">5000</property>
                     <property name="timeToLiveSeconds">1000</property>
                     <property name="minTimeToLiveSeconds">120</property>
                  </bean>
               </property>
            </bean>
         </property>
         <property name="evictionRegionConfigs">
            <list>
               <bean class="org.jboss.cache.config.EvictionRegionConfig">
                  <property name="regionName">/myprefix/Account</property>
                  <property name="evictionAlgorithmConfig">
                     <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                        <property name="maxNodes">10000</property>
                        <property name="timeToLiveSeconds">5000</property>
                        <property name="minTimeToLiveSeconds">120</property>
                     </bean>
                  </property>
               </bean>
               <bean class="org.jboss.cache.config.EvictionRegionConfig">
                  <property name="regionName">/myprefix/org/hibernate/cache/StandardQueryCache</property>
                  <property name="evictionAlgorithmConfig">
                     <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                        <property name="maxNodes">100</property>
                        <property name="timeToLiveSeconds">600</property>
                        <property name="minTimeToLiveSeconds">120</property>
                     </bean>
                  </property>
               </bean>
            </list>
         </property>
      </bean>
   </property>
</bean>
上記の @NamedQuery.hints 属性はベンダー固有の @QueryHints の配列を値とします。 Hibernate は "org.hibernate.cacheRegion" クエリヒントを許可します。 この値はデフォルトの /org/hibernate/cache/StandardQueryCache の代わりに使用するキャッシュリージョンの名前です。 例は次の通りです。
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
  @NamedQuery(
    name = "account.bybranch",
    query = "select acct from Account as acct where acct.branch = ?1",
    hints = 
    {
      @QueryHint(name = "org.hibernate.cacheable", value = "true"),
      @QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")
    }
  )
})
public class Account implements Serializable
{
  // ... ... 
}
関連する除外設定:
<bean name="..." class="org.jboss.cache.config.Configuration">
    ... ...
   <property name="evictionConfig">
       <bean class="org.jboss.cache.config.EvictionConfig">
         <property name="wakeupInterval">5000</property>
         <!--  Overall default -->
         <property name="defaultEvictionRegionConfig">
            <bean class="org.jboss.cache.config.EvictionRegionConfig">
               <property name="regionName">/</property>
               <property name="evictionAlgorithmConfig">
                  <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                     <property name="maxNodes">5000</property>
                     <property name="timeToLiveSeconds">1000</property>
                     <property name="minTimeToLiveSeconds">120</property>
                  </bean>
               </property>
            </bean>
         </property>
         <property name="evictionRegionConfigs">
            <list>
               <bean class="org.jboss.cache.config.EvictionRegionConfig">
                  <property name="regionName">/myprefix/Account</property>
                  <property name="evictionAlgorithmConfig">
                     <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                        <property name="maxNodes">10000</property>
                        <property name="timeToLiveSeconds">5000</property>
                        <property name="minTimeToLiveSeconds">120</property>
                     </bean>
                  </property>
               </bean>
               <bean class="org.jboss.cache.config.EvictionRegionConfig">
                  <property name="regionName">/myprefix/Queries</property>
                  <property name="evictionAlgorithmConfig">
                     <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                        <property name="maxNodes">100</property>
                        <property name="timeToLiveSeconds">600</property>
                        <property name="minTimeToLiveSeconds">120</property>
                     </bean>
                  </property>
               </bean>
                ... ...
            </list>
         </property>
      </bean>
   </property>
</bean>