Hibernate キャッシング – 一次キャッシュ

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

Hibernateキャッシング

Hibernateキャッシュは、正しく使用される場合、アプリケーションのパフォーマンス向上に非常に役立ちます。キャッシュのアイデアは、データベースクエリの数を減らし、それによりアプリケーションのスループット時間を減らすことです。Hibernateにはさまざまな種類のキャッシュが付属しています:

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

Hibernateキャッシュ – ファーストレベルキャッシュの例

私のハイバネートの一次キャッシュの例のプログラムでは、HQLの例と同じ構成を使用しています。それを確認して、テーブルを構成し、ダミーデータで満たしてください。まずプログラムとその出力を見てから、ハイバネートの一次キャッシュに関連する重要なポイントをいくつか説明します。 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();
		
		//IDが1の従業員を取得
		Employee emp = (Employee) session.load(Employee.class, new Long(1));
		printData(emp,1);
		
		//バックエンドのデータが変更されるのを少し待つ
		Thread.sleep(10000);
		
		//同じデータを再度取得し、クエリが発行されていないことをログで確認する
		Employee emp1 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp1,2);
		
		//新しいセッションを作成
		Session newSession = sessionFactory.openSession();
		//IDが1の従業員を取得, notice the logs for query
		Employee emp2 = (Employee) newSession.load(Employee.class, new Long(1));
		printData(emp2,3);
		
		//START: evictの例、ハイバネートの一次キャッシュから特定のオブジェクトを削除するためのevict
		//IDが2の従業員を取得、初回のためログにクエリが表示される
		Employee emp3 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp3,4);
		
		//IDが1の従業員オブジェクトをevictする
		session.evict(emp);
		System.out.println("Session Contains Employee with id=1?"+session.contains(emp));

		//オブジェクトが一次キャッシュから削除されたため、ログにクエリが表示される
		Employee emp4 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp4,5);
		
		//このオブジェクトはまだ存在しているため、ログにクエリは表示されません
		Employee emp5 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp5,6);
		//END: evictの例
		
		//START: clearの例、一次キャッシュからすべてを削除するためのclear
		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

ハイバネートのファーストレベルキャッシュの重要なポイント

上記のプログラムから導き出されるハイバネートのファーストレベルキャッシュに関する重要なポイントは次のとおりです:

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

ハイバネートのキャッシングとファーストレベルキャッシュの例については以上です。今後の投稿では、ハイバネートのセカンドレベルキャッシュ – EHCacheの実装について調べていきます。

Source:
https://www.digitalocean.com/community/tutorials/hibernate-caching-first-level-cache