Hibernate Caching – 첫 번째 레벨 캐시

Hibernate 캐싱에 오신 것을 환영합니다 – 첫 번째 레벨 캐시 예제 튜토리얼입니다. 최근에 우리는 하이버네이트 아키텍처, 하이버네이트 매핑 그리고 객체지향적 방식으로 SQL 쿼리를 실행하기 위해 HQL을 사용하는 방법에 대해 살펴보았습니다. 오늘은 하이버네이트의 중요한 측면 중 하나인 하이버네이트 캐시에 대해 알아보겠습니다.

하이버네이트 캐싱

하이버네이트 캐시는 올바르게 사용될 경우 빠른 애플리케이션 성능을 얻는 데 매우 유용할 수 있습니다. 캐시의 아이디어는 데이터베이스 쿼리의 수를 줄이는 것이며, 이로 인해 애플리케이션의 처리 시간이 줄어듭니다. 하이버네이트는 다양한 유형의 캐시를 제공합니다:

  1. 첫 번째 레벨 캐시: 하이버네이트의 첫 번째 레벨 캐시는 세션 객체와 관련이 있습니다. 하이버네이트의 첫 번째 레벨 캐시는 기본적으로 활성화되어 있으며 비활성화하는 방법이 없습니다. 그러나 하이버네이트는 캐시에서 특정 객체를 삭제하거나 캐시를 완전히 지울 수 있는 메서드를 제공합니다. 세션에 캐시된 모든 객체는 다른 세션에서 볼 수 없으며 세션이 닫힐 때 모든 캐시된 객체도 손실됩니다.
  2. 두 번째 레벨 캐시: 하이버네이트 두 번째 레벨 캐시는 기본적으로 비활성화되어 있지만 설정을 통해 활성화할 수 있습니다. 현재 EHCache와 Infinispan이 하이버네이트 두 번째 레벨 캐시를 위한 구현을 제공하며 사용할 수 있습니다. 하이버네이트 캐싱에 대한 다음 자습서에서 살펴보겠습니다.
  3. 쿼리 캐시: 하이버네이트는 쿼리의 결과 집합도 캐시할 수 있습니다. 하이버네이트 쿼리 캐시는 캐시에 실제 엔터티의 상태를 캐시하지 않습니다. 대신 식별자 값과 값 유형의 결과만 캐시합니다. 따라서 항상 두 번째 레벨 캐시와 함께 사용해야 합니다.

하이버네이트 캐싱 – 첫 번째 레벨 캐시 예제

내 하이버네이트 첫 번째 레벨 캐시 예제 프로그램에서는 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();
		
		//아이디가 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();
		//아이디가 1인 직원 가져오기, notice the logs for query
		Employee emp2 = (Employee) newSession.load(Employee.class, new Long(1));
		printData(emp2,3);
		
		//시작: 하이버네이트 첫 번째 레벨 캐시에서 특정 객체를 제거하려면 예시
		//아이디가 2인 직원 가져오기, 첫 번째 시도이므로 로그에 쿼리 표시
		Employee emp3 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp3,4);
		
		//아이디가 1인 직원 객체 제거
		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);
		//종료: 제거 예시
		
		//시작: 첫 번째 레벨 캐시에서 모든 것을 제거하려면 예시
		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초 동안 일시 중지하고 그 동안 데이터베이스에서 값을 업데이트했지만 동일한 세션에서는 반영되지 않았습니다. 그러나 다른 세션에서는 업데이트된 값을 얻었습니다.
  4. 우리는 세션 evict() 메서드를 사용하여 하이버네이트의 첫 번째 레벨 캐시에서 단일 개체를 제거할 수 있습니다.
  5. 세션 clear() 메서드를 사용하여 캐시를 지울 수 있으며, 이는 캐시에서 모든 개체를 삭제합니다.
  6. 세션 contains() 메서드를 사용하여 하이버네이트 캐시에 객체가 있는지 확인할 수 있습니다. 객체가 캐시에서 찾아지면 true를 반환하고 그렇지 않으면 false를 반환합니다.
  7. 하이버네이트는 모든 객체를 세션의 첫 번째 레벨 캐시에 캐시하므로 대량 쿼리 또는 일괄 업데이트를 실행하는 동안 일정한 간격으로 캐시를 지우는 것이 메모리 문제를 피하기 위해 필요합니다.

이것이 하이버네이트 캐싱과 첫 번째 레벨 캐시 예제에 대한 모든 것입니다. 향후 게시물에서는 Hibernate Second Level Cache – EHCache 구현을 살펴볼 것입니다.

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