영속성 컨텍스트의 이점 - 1차 캐시
"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
지난번 글에 이어서 이번엔 영속성 컨텍스트를 사용해서 얻을 수 있는 이점들에 대해서 알아보자.
1. 1차 캐시로서 영속성 컨텍스트 사용가능
2. 자바 컬렉션과 같은 동일성(identify) 비교 보장( == 비교)
3. 트랜잭션을 지원하는 버퍼링과 같은 쓰기 지연(transactional wrtie-behind)
4. 변경 감지(Dirty Checking)
5. 지연 로딩(Lazy Loading)
1. 영속성 컨텍스트의 1차 캐시로서의 활용
영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에 중간계층으로서 존재하는 동시에 1차 캐시를 내부적으로 가지고 있다.
- Entity 를 persist 메소드를 통해 영속 상태로 만들경우 발생하는 일
: Entity 는 본인이 원래 가지고 있던 Id 값과 value 값 그대로 영속성 컨텍스트 내부의 1차캐시에 저장된다.
위와 같이 1차 캐시에 Entity 가 저장될 경우 이점은 다음과 같다.
- 조회
find 메소드를 통해 Entity 를 조회할 경우 JPA 는 우선 영속성 컨텍스트 에서 1차 캐시를 찾는다.
그 결과 찾고자 하는 Entity 가 있을 경우 1차 캐시에 있는 값을 그냥 조회해서 가져오게 된다.
그런데 1차 캐시에 없다면 그때 데이터베이스에 가서 조회하고, 해당 객체를 1차 캐시에 저장한 후 조회한 Entity 를 반환해준다.
이후 반복해서 다시 조회하게 될 경우 데이터베이스를 조회하는 것이 아니라 1차 캐시에서 곧바로 발견하여 반환할 수 있게 된다.
* 중요한 점
사실 EntityManager 는 데이터베이스 트랜잭션을 단위로 만들고 데이터베이스 트랜잭션이 끝나면 같이 종료시켜 버린다.
말 그대로, 고객의 요청이 들어온 이후 그 요청이 끝나버리면 영속성 컨텍스트를 지운다는 얘기이다.
즉, 1차 캐시도 함께 지워진다.
한 마디로 고객의 요청이 들어오고 있는 찰나의 순간에만 이점이 있다는 것이다.(여러명의 고객이 함께 사용하는 캐시가 아니라는 뜻)
애플리케이션 전체에서 공유하는 캐시는 JPA hibernate 에서는 2차 캐시라고 부른다.
1차 캐시는 데이터베이스의 한 트랜잭션 안에서만 효과가 있기 때문에 성능의 이점을 크게 얻을 수 있는 것은 아니다.
(그러나 비즈니스 로직이 굉장히 복잡한 경우엔 이점이 생길 수 있다.)
* 영속성 컨텍스트 내부의 1차 캐시를 활용하는 것에 대한 예시
1. 이미 삽입 되어 있는 데이터를 find 메소드를 이용하여 연속해서 두번 찾는다.
2. 이후 출력을 통해 데이터를 제대로 찾아왔는지 확인함과 동시에 hibernate 에서 SQL 문이 어떻게 작성되어 데이터베이스로 쿼리를 전달하는지 확인해본다.
- hibernate 에서 SQL 문을 어떻게 작성해주는 지 확인하고 싶다면 아래의 옵션을 persistence.xml 파일에 추가하면 된다.
<property name="hibernate.show_sql" value="true"/> <!-- 콘솔에 하이버네이트가 실행하는 SQL문 출력 -->
<property name="hibernate.format_sql" value="true"/> <!-- SQL 출력 시 보기 쉽게 정렬 -->
<property name="hibernate.use_sql_comments" value="true"/> <!-- 쿼리 출력 시 주석(comments)도 함께 출력 -->
Member findMemeber1 = em.find(Member.class, 101L); // SELECT query 실행
Member findMemeber2 = em.find(Member.class, 101L); // 같은 데이터를 찾는 경우이기 때문에 1차캐시 에서 데이터를 가져오게 된다.(SELECT query 실행되지 않음)
System.out.println("findMemeber.id = " + findMemeber1.getId());
System.out.println("findMemeber.name = " + findMemeber2.getName());
System.out.println("result = " + (findMemeber1 == findMemeber2));
2의 과정을 거쳤을 때 보통 find 메소드로 인해 SELECT query 가 2번 생성되어야 할 것이라고 생각하지만, 앞전에 조회했던 데이터가 1차 캐시에 들어가 있던 상황에서 같은 데이터를 또 한번 참조 하였으므로 별도의 SELECT query 없이 곧바로 데이터가 찾아지는 것을 확인할 수 있다.
(실행 콘솔에 2개의 SELECT query 가 나타나지 않는 것으로 확인할 수 있다.)
JPA 는 Entity 를 조회하거나, 하고 나면 무조건 해당 Entity 를 영속성 컨텍스트 안에 올린다.
이후 똑같은 것을 조회하게 될 경우 영속성 컨텍스트 안에 있는 1차 캐시부터 찾게 되면서, 그 안에 있던 Entity 를 반환함으로서 SELECT 문을 다시 전달하지 않게 되는 것이다.
정말 비즈니스 로직이 복잡한 경우가 아니면 성능에 큰 도움이 되는 것은 아니나, 성능적인 이점보다는 컨셉을 이해하는 것에 있어(좀 더 객체지향적으로 코드를 작성하는 것에 있어서) 이점을 가져올수 있다.