ウェブサイト検索

ハイバーネートキャッシング - 第一レベルキャッシュ


Hibernateキャッシングへようこそ - ファーストレベルキャッシュの例チュートリアル。最近、Hibernateのアーキテクチャ、Hibernateマッピング、オブジェクト指向的にSQLクエリを発行するためのHQLの使い方について見てきました。今日はHibernateの重要な側面の一つであるHibernateキャッシュについて見ていきます。

ハイバーネートキャッシング

Hibernateキャッシュは、正しく使用すればアプリケーションのパフォーマンスを高速化するのに非常に役立ちます。キャッシュの背後にあるアイデアは、データベースクエリの数を減らすことであり、その結果、アプリケーションの処理時間を短縮することです。Hibernateには、さまざまなタイプのキャッシュが用意されています。

  1. ファーストレベルキャッシュ: Hibernateのファーストレベルキャッシュはセッションオブジェクトに関連しています。Hibernateのファーストレベルキャッシュはデフォルトで有効になっており、無効にする方法はありません。しかし、Hibernateは特定のオブジェクトをキャッシュから削除するか、キャッシュを完全にクリアするためのメソッドを提供しています。セッション内にキャッシュされたオブジェクトは他のセッションからは見えず、セッションが閉じられるとすべてのキャッシュされたオブジェクトも失われます。
  2. セカンドレベルキャッシュ: Hibernateのセカンドレベルキャッシュはデフォルトで無効になっていますが、設定を通じて有効にすることができます。現在、EHCacheとInfinispanがHibernateセカンドレベルキャッシュの実装を提供しており、これらを使用することができます。この内容については、次回のHibernateキャッシングに関するチュートリアルで見ていきましょう。
  3. クエリキャッシュ: Hibernateは、クエリの結果セットをキャッシュすることもできます。Hibernateクエリキャッシュは、キャッシュ内の実際のエンティティの状態をキャッシュするのではなく、識別子の値と値タイプの結果のみをキャッシュします。したがって、常に第二レベルキャッシュと併用する必要があります。

Hibernateキャッシング - 第一レベルキャッシュの例

私のHibernateファーストレベルキャッシュの例プログラムでは、HQLの例と同じ設定を使用しています。それを確認してテーブルを設定し、ダミーデータで埋めることができます。まずプログラムとその出力を見てから、Hibernateファーストレベルキャッシュに関連するいくつかの重要な点を見ていきましょう。HibernateCacheExample.java

package com.journaldev.hibernate.main;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateCacheExample {

	public static void main(String[] args) throws InterruptedException {
		
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		
		//Get employee with id=1
		Employee emp = (Employee) session.load(Employee.class, new Long(1));
		printData(emp,1);
		
		//waiting for sometime to change the data in backend
		Thread.sleep(10000);
		
		//Fetch same data again, check logs that no query fired
		Employee emp1 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp1,2);
		
		//Create new session
		Session newSession = sessionFactory.openSession();
		//Get employee with id=1, notice the logs for query
		Employee emp2 = (Employee) newSession.load(Employee.class, new Long(1));
		printData(emp2,3);
		
		//START: evict example to remove specific object from hibernate first level cache
		//Get employee with id=2, first time hence query in logs
		Employee emp3 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp3,4);
		
		//evict the employee object with id=1
		session.evict(emp);
		System.out.println("Session Contains Employee with id=1?"+session.contains(emp));

		//since object is removed from first level cache, you will see query in logs
		Employee emp4 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp4,5);
		
		//this object is still present, so you won't see query in logs
		Employee emp5 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp5,6);
		//END: evict example
		
		//START: clear example to remove everything from first level cache
		session.clear();
		Employee emp6 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp6,7);
		Employee emp7 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp7,8);
		
		System.out.println("Session Contains Employee with id=2?"+session.contains(emp7));
		
		tx.commit();
		sessionFactory.close();
	}

	private static void printData(Employee emp, int count) {
		System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
	}

}

上記の例を実行すると、出力には多くのハイバーネートに関連する情報が含まれています。しかし、私たちは主に、私たちのコード固有の出力と、データをロードするためにハイバーネートが発行したクエリに関心があります。出力のスニペットは以下のようになります。

Hibernate Configuration loaded
Hibernate serviceRegistry created
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
2:: Name=Pankaj, Zipcode=95129
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=?
3:: Name=PankajK, Zipcode=95129
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=David, Zipcode=95051
Session Contains Employee with id=1?false
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=?
5:: Name=Pankaj, Zipcode=95129
6:: Name=David, Zipcode=95051
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=?
7:: Name=Pankaj, Zipcode=95129
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=?
8:: Name=David, Zipcode=95051
Session Contains Employee with id=2?true

Hibernateのファーストレベルキャッシュの重要ポイント

上記のプログラムから導き出されるHibernateの第一レベルキャッシュに関する重要なポイントは次のとおりです。

  1. Hibernateの第一レベルキャッシュはデフォルトで有効になっており、このための設定は必要ありません。
  2. Hibernateの第一レベルキャッシュはセッション固有です。だから、同じセッションで同じデータを取得する際にはクエリが実行されず、別のセッションではデータをロードするためにクエリが実行されます。
  3. Hibernateのファーストレベルキャッシュには古い値が含まれている可能性があります。上記のように、私はプログラムを10秒間スリープさせ、その間にデータベースで値(名前をPankajからPankajKに)を更新しましたが、同じセッションではその変更が反映されませんでした。しかし、別のセッションでは更新された値を取得することができました。
  4. セッションのevict()メソッドを使用して、Hibernateの第一レベルキャッシュから単一のオブジェクトを削除できます。
  5. セッションの clear() メソッドを使用して、キャッシュをクリアつまり、キャッシュ内のすべてのオブジェクトを削除することができます。
  6. セッションの contains() メソッドを使用して、オブジェクトがハイバーネートキャッシュに存在するかどうかを確認できます。オブジェクトがキャッシュ内に見つかった場合はtrueを返し、見つからなかった場合はfalseを返します。
  7. Hibernateはすべてのオブジェクトをセッションのファーストレベルキャッシュにキャッシュするため、大量のクエリやバッチ更新を実行する際には、メモリの問題を避けるために一定の間隔でキャッシュをクリアする必要があります。

これがHibernateキャッシングとファーストレベルキャッシュの例についてのすべてです。今後の投稿では、Hibernateセカンドレベルキャッシュ - EHCacheの実装について見ていきます。

関連記事: