ネイティブの日本語で以下を言い換えます。オプションは1つで構いません。Hibernate EHCache – Hibernate のセカンドレベルキャッシュ
「Hibernate Second Level Cacheの例のチュートリアルへようこそ。今日は、最も人気のあるHibernate Second Level CacheプロバイダであるHibernate EHCacheについて見ていきましょう。」
ヒバネイトのセカンドレベルキャッシュ
大規模なアプリケーションでHibernateを使用する主な利点の一つは、キャッシュのサポートによりデータベースクエリの削減とより優れたパフォーマンスが得られることです。以前の例では、Hibernateのファーストレベルキャッシュを見てきましたが、今日はHibernate EHCacheの実装を使用したヒバネートセカンドレベルキャッシュについて見ていきます。HibernateセカンドレベルキャッシュのプロバイダにはEHCacheとInfinispanがありますが、EHCacheがより人気があり、この例のプロジェクトではそれを使用します。ただし、プロジェクトに移る前に、オブジェクトをキャッシュするための異なる戦略を知っておく必要があります。
-
- 読み取り専用: このキャッシュ戦略は、常に読み取りが行われるが更新されない永続オブジェクトに使用されるべきです。アプリケーションの設定やその他の静的データの読み取りとキャッシュに適しています。データベースでオブジェクトが更新されたかどうかを確認するオーバーロードがないため、最も単純でパフォーマンスが最も良い戦略です。
読み書き: Hibernateアプリケーションによって更新可能な永続オブジェクトに適しています。ただし、バックエンドまたは他のアプリケーションを介してデータが更新された場合、Hibernateはそれを知る方法がなく、データが古くなる可能性があります。この戦略を使用する場合は、データの更新にHibernate APIを使用していることを確認してください。
非制約型読み書き: アプリケーションがデータを時折更新する必要があり、厳格なトランザクション分離が必要ではない場合、非制約型の読み書きキャッシュが適している場合があります。
トランザクショナル: トランザクションキャッシュ戦略は、JBoss TreeCacheなどの完全なトランザクショナルキャッシュプロバイダーのサポートを提供します。このようなキャッシュはJTA環境でのみ使用でき、hibernate.transaction.manager_lookup_classを指定する必要があります。
ハイバネートのEHCache
以下は、EHCacheがすべてのキャッシュ戦略をサポートしているため、Hibernateでセカンドレベルキャッシュを探している場合には最良の選択肢です。EHCacheについては詳細に触れませんが、Hibernateアプリケーションでの動作を重点的に取り上げます。Eclipseやお気に入りのIDEでMavenプロジェクトを作成し、最終的な実装は以下の画像のようになります。それでは、アプリケーションの各コンポーネントを一つずつ見ていきましょう。
ネイティブの日本語で以下を言い換えます。1つのオプションだけで良いです:
ヒベルネイトのEHCacheのMaven依存関係
ハイバネイトのセカンドレベルキャッシュには、アプリケーションにehcache-coreとhibernate-ehcacheの依存関係を追加する必要があります。EHCacheはログ出力にslf4jを使用しているため、ログ目的でslf4j-simpleも追加しました。これらのAPIの最新バージョンを使用していますが、hibernate-ehcacheのAPIがehcache-coreのAPIと互換性がない場合、pom.xmlのhibernate-ehcacheを確認して正しいバージョンを使用する必要があります。最終的なpom.xmlは以下のようになります。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.scdev.hibernate</groupId>
<artifactId>HibernateEHCacheExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Hibernate Secondary Level Cache Example using EHCache implementation</description>
<dependencies>
<!-- Hibernate Core API -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
<!-- EHCache Core APIs -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.9</version>
</dependency>
<!-- Hibernate EHCache API -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.5.Final</version>
</dependency>
<!-- EHCache uses slf4j for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
</project>
ネイティブな日本語で次の内容を言い換えますが、1つのオプションだけで大丈夫です:
Hibernateのセカンドレベルキャッシュ- Hibernate EHCacheの設定
デフォルトでは、Hibernateのセカンドレベルキャッシュは無効になっているため、有効にするためにはいくつかの設定を追加する必要があります。私たちのhibernate.cfg.xmlファイルは以下のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">scdev123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">scdev</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- For singleton factory -->
<!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
-->
<!-- enable second level cache and query cache -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>
<mapping class="com.scdev.hibernate.model.Employee" />
<mapping class="com.scdev.hibernate.model.Address" />
</session-factory>
</hibernate-configuration>
ハイバネートのセカンドレベルキャッシュの設定についての重要なポイントは以下の通りです。
-
- hibernate.cache.region.factory_classは、セカンドレベルのキャッシュのためのファクトリクラスを定義するために使用されます。私はこれにorg.hibernate.cache.ehcache.EhCacheRegionFactoryを使用しています。ファクトリクラスをシングルトンにしたい場合は、org.hibernate.cache.ehcache.SingletonEhCacheRegionFactoryクラスを使用する必要があります。Hibernate 3を使用している場合、対応するクラスはnet.sf.ehcache.hibernate.EhCacheRegionFactoryとnet.sf.ehcache.hibernate.SingletonEhCacheRegionFactoryになります。
hibernate.cache.use_second_level_cacheは、セカンドレベルキャッシュを有効にするために使用されます。
hibernate.cache.use_query_cacheは、クエリキャッシュを有効にするために使用されます。これがないと、HQLクエリの結果はキャッシュされません。
net.sf.ehcache.configurationResourceNameは、EHCacheの設定ファイルの場所を定義するために使用されます。これはオプションのパラメータであり、存在しない場合、EHCacheはアプリケーションのクラスパスでehcache.xmlファイルを検索しようとします。
ハイバネートのEHCache設定ファイルの構成
私たちのEHCache設定ファイルであるmyehcache.xmlは以下のようになっています。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="java.io.tmpdir/ehcache" />
<defaultCache maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" statistics="true">
<persistence strategy="localTempSwap" />
</defaultCache>
<cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="5" timeToLiveSeconds="10">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxEntriesLocalHeap="5000" eternal="true">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
ネイティブの日本語で以下の文章を言い換えます。1つのオプションのみ必要です。
ハイバネートのEHCacheは多くのオプションを提供していますが、詳細には立ち入りませんが、上記の重要な設定のいくつかは次のとおりです。
-
- ディスクストア:EHCacheはデータをメモリに保存しますが、オーバーフローが発生すると、データをファイルシステムに書き込むようになります。 EHCacheがオーバーフローしたデータを書き込む場所を定義するために、このプロパティを使用しています。
defaultCache:これは必須の設定であり、オブジェクトをキャッシュする必要があり、そのためにキャッシュリージョンが定義されていない場合に使用されます。
cache name=”employee”:キャッシュ要素を使用してリージョンとその設定を定義します。複数のリージョンとそのプロパティを定義することができます。モデルビーンのキャッシュプロパティを定義する際に、キャッシュストラテジー付きのリージョンも定義することができます。キャッシュプロパティは名前を付けることで理解しやすく明確です。
EHCacheへの警告に対応するために、Cache regions org.hibernate.cache.internal.StandardQueryCacheとorg.hibernate.cache.spi.UpdateTimestampsCacheが定義されています。
ハイバネートのセカンドレベルキャッシュ – モデルビーンのキャッシュストラテジー
私たちは、キャッシュの設定を提供するために、org.hibernate.annotations.Cacheアノテーションを使用しています。org.hibernate.annotations.CacheConcurrencyStrategyを使用して、キャッシュの戦略を定義することができ、またモデルのビーンに使用するキャッシュ領域を定義することもできます。
package com.scdev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name = "ADDRESS")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Address {
@Id
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
private long id;
@Column(name = "address_line1")
private String addressLine1;
@Column(name = "zipcode")
private String zipcode;
@Column(name = "city")
private String city;
@OneToOne
@PrimaryKeyJoinColumn
private Employee employee;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
package com.scdev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;
@Column(name = "emp_name")
private String name;
@Column(name = "emp_salary")
private double salary;
@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
private Address address;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
HQLの例と同じデータベースセットアップを使用していることに注意してください。データベーステーブルを作成し、サンプルデータを読み込むためにそれを確認することをお勧めします。
ハイバネイトのセッションファクトリーユーティリティクラス
私たちは、Hibernateを設定し、SessionFactoryのシングルトンインスタンスを取得するためのシンプルなユーティリティクラスを持っています。
package com.scdev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
System.out.println("Hibernate Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
私たちのHibernate EHCacheを使用した二次レベルキャッシュプロジェクトが準備できましたので、それをテストするために簡単なプログラムを書きましょう。
ネイティブの日本語で次の文を言い換えてください(一つのオプションのみで結構です):
HibernateのEHCacheテストプログラム
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.stat.Statistics;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernateEHCacheMain {
public static void main(String[] args) {
System.out.println("Temp Dir:"+System.getProperty("java.io.tmpdir"));
//Initialize Sessions
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Statistics stats = sessionFactory.getStatistics();
System.out.println("Stats enabled="+stats.isStatisticsEnabled());
stats.setStatisticsEnabled(true);
System.out.println("Stats enabled="+stats.isStatisticsEnabled());
Session session = sessionFactory.openSession();
Session otherSession = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Transaction otherTransaction = otherSession.beginTransaction();
printStats(stats, 0);
Employee emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 1);
emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 2);
//clear first level cache, so that second level cache is used
session.evict(emp);
emp = (Employee) session.load(Employee.class, 1L);
printData(emp, stats, 3);
emp = (Employee) session.load(Employee.class, 3L);
printData(emp, stats, 4);
emp = (Employee) otherSession.load(Employee.class, 1L);
printData(emp, stats, 5);
//Release resources
transaction.commit();
otherTransaction.commit();
sessionFactory.close();
}
private static void printStats(Statistics stats, int i) {
System.out.println("***** " + i + " *****");
System.out.println("Fetch Count="
+ stats.getEntityFetchCount());
System.out.println("Second Level Hit Count="
+ stats.getSecondLevelCacheHitCount());
System.out
.println("Second Level Miss Count="
+ stats
.getSecondLevelCacheMissCount());
System.out.println("Second Level Put Count="
+ stats.getSecondLevelCachePutCount());
}
private static void printData(Employee emp, Statistics stats, int count) {
System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
printStats(stats, count);
}
}
Hibernateのorg.hibernate.stat.Statisticsクラスは、HibernateのSessionFactoryの統計情報を提供しています。私たちはそれを使用して、フェッチカウントやセカンドレベルのキャッシュのヒット、ミス、およびプットのカウントを表示しています。パフォーマンスを向上させるために、統計情報はデフォルトで無効になっていますので、プログラムの開始時に有効にしています。上記のプログラムを実行すると、HibernateとEHCacheのAPIによって多くの出力が生成されますが、私たちは印刷しているデータに興味があります。サンプルの実行では、以下の出力が表示されます。
Temp Dir:/var/folders/h4/q73jjy0902g51wkw0w69c0600000gn/T/
Hibernate Configuration loaded
Hibernate serviceRegistry created
Stats enabled=false
Stats enabled=true
***** 0 *****
Fetch Count=0
Second Level Hit Count=0
Second Level Miss Count=0
Second Level Put Count=0
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
1:: Name=Pankaj, Zipcode=95129
***** 1 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
2:: Name=Pankaj, Zipcode=95129
***** 2 *****
Fetch Count=1
Second Level Hit Count=0
Second Level Miss Count=1
Second Level Put Count=2
3:: Name=Pankaj, Zipcode=95129
***** 3 *****
Fetch Count=1
Second Level Hit Count=2
Second Level Miss Count=1
Second Level Put Count=2
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
4:: Name=Lisa, Zipcode=560100
***** 4 *****
Fetch Count=2
Second Level Hit Count=2
Second Level Miss Count=2
Second Level Put Count=4
5:: Name=Pankaj, Zipcode=95129
***** 5 *****
Fetch Count=2
Second Level Hit Count=4
Second Level Miss Count=2
Second Level Put Count=4
出力からわかるように、最初は統計情報が無効にされていましたが、私たちはハイバネートのセカンドレベルキャッシュを確認するために有効化しました。出力のステップバイステップの説明は以下の通りです。
-
- 私たちのアプリケーションにデータをロードする前は、すべての統計値は予想通りに0です。
-
- 最初に、IDが1の従業員をロードする場合、まずはファーストレベルキャッシュ、次にセカンドレベルキャッシュを検索します。キャッシュに見つからない場合、データベースクエリが実行され、フェッチ数が1になります。オブジェクトがロードされると、ファーストレベルキャッシュとセカンドレベルキャッシュの両方に保存されます。それによって、セカンドレベルのヒット数は0のままで、ミス数が1になります。注意すべきは、プット数が2であることです。なぜなら、従業員オブジェクトにはアドレスも含まれているため、両方のオブジェクトがセカンドレベルキャッシュに保存されていて、数が2に増えているからです。
-
- 次に、再びIDが1の従業員をロードしますが、この時はファーストレベルキャッシュに存在しています。したがって、データベースクエリは表示されず、他のセカンドレベルキャッシュの統計値も変わりません。
-
- 次に、evict()メソッドを使用してファーストレベルキャッシュから従業員オブジェクトを削除し、それをロードしようとすると、Hibernateはセカンドレベルキャッシュで見つけます。そのため、データベースクエリは実行されず、フェッチ数は1のままです。ヒット数は0から2に増えることに注意してください。なぜなら、従業員オブジェクトとアドレスオブジェクトの両方がセカンドレベルキャッシュから読み込まれたからです。セカンドレベルのミス数とプット数は以前の値のままです。
-
- 次に、IDが3の従業員をロードします。データベースクエリが実行され、フェッチ数が2に増え、ミス数は1から2に増え、プット数は2から4に増えます。
- 次に、別のセッションでIDが1の従業員をロードしようとします。Hibernateのセカンドレベルキャッシュはセッション間で共有されているため、セカンドレベルキャッシュで見つかり、データベースクエリは実行されません。フェッチ数、ミス数、プット数は変わらず、ヒット数は2から4に増えます。
私たちのHibernateの第2レベルキャッシュであるHibernate EHCacheは正常に機能していることは明らかです。Hibernateの統計情報は、システムのボトルネックを見つけて最適化し、フェッチ回数を減らしキャッシュからデータをより多くロードするのに役立ちます。これがHibernate EHCacheの例のすべてです。EHCacheをHibernateアプリケーションに設定し、Hibernateの第2レベルキャッシュを通じてより優れたパフォーマンスを得るのに役立つことを願っています。サンプルプロジェクトは以下のリンクからダウンロードし、他の統計データを使用してさらに学ぶことができます。
ヒベルネートEHCacheプロジェクトをダウンロードしてください。