스프링/JPA

JPA - 영속성 컨텍스트 (2022.12.11 추가)

지팡구 2022. 12. 9. 17:09

전에 JPA가 무엇인지 간략하게 짚고 넘어갔고, 이번엔 왜 JPA가 중요한지에 대해 짚고 넘어가고자 한다.

JPA가 부여받은 임무는 다음과 같다.

 

  • 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
  • 영속성 컨텍스트

 

객체와 RDB를 Mapping? 영속성 컨텍스트? 대체 이 것들이 무엇일까? 왜 JPA에서 이 두가지가 중요한지 궁금증이 생길 것이다. 우선 영속성 컨텍스트에 대해 짚고 넘어가야 한다. 


< 목차 >

1. 영속성 컨텍스트란 무엇인가?

2. Entity 생명 주기

3. 영속성 컨텍스트의 장점이자 이점


 

1. 영속성 컨텍스트? 

영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에 데이터를 보관하는 논리적 개념인데,

쉽게 설명하면 "엔티티를 영구 저장하는 환경" 이라는 뜻을 가지고 있다.

 

우리가 웹 애플리케이션을 개발하게되면 ,Entity를 관리해줄 EntityManagerFactory라는 놈을 통해서 사용자의 요청이 들어올 때마다 EntityManager를 생성하고, Entity를 저장하는 과정을 거치게 된다. 이때 일괄적으로 작업을 진행하는 것이 아니라 요청이 들어올 때마다 이러한 행위들을 반복하게 된다.

 

위의 예시에서 설명한 것처럼 영속성 컨텍스트는 데이터 객체를 보관하는 역할을 진행하게 되고, Entity Manager를 통해 영속성 컨텍스트에 접근한다. 


2. Entity의 생명 주기
  • 비영속(New)

비영속은 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태를 말하며, 실제 DB 데이터와 관련없는, 그냥 Java의 객체 상태

  • 영속(managed)

엔티티 매니저를 통해 영속성 컨텍스트에서 관리되는 상태로 변경 감지를 통해 JPA가 이를 추척하고 DB에 반영한다. 이 때, 커밋 혹은 flush() 이후에 DB에 저장된다는 점을 알고 있어야 하고, 그 전까진 영속성 컨텍스트에만 존재한다.

  • 준영속(Detached)

영속성 컨텍스트에서 저장되었다가 분리된 상태

  • 삭제(removed)

삭제된 상태

 

출처 : 인프런 김영한 자바 ORM 표준 JPA


3. 영속성 컨텍스트의 장점이자 이점
  • 1차 캐시

백엔드에서는 DB와 연관된 작업을 진행하게 되는데, 이러한 작업들은 상대적으로 많은 부하와 리소스를 사용하게 된다. 그래서 이러한 작업들을 줄이는 것이 백엔드에서 지향할 점인데, 영속성 컨텍스트 내부에서 1차 캐시를 둬서 데이터를 다루는 작업이 발생하면 먼저 캐시 내부에서 찾고, 없다면 쿼리문을 통해 데이터를 불러오고, 이 데이터를 다시 캐시 내부에 저장 후 반환하는 작업을 하게된다.

 

그래서 그 이후 비슷한 작업을 진행할 때, 먼저 DB로 쿼리 문을 날리는 것이 아니라 캐시 내부를 확인하며 굳이 DB를 조회하는 작업이 없으니 성능상으로 이점이 발생한다.

 

 

  • 동일성( identity ) 보장

같은 호출을 반복하게 되면 영속성 컨텍스트는 1차 캐시에 있는 같은 인스턴스를 반환하는데, 이로서 영속성 엔티티의 동일성을 보장해주며  (==)를 이용한 비교가 가능하다. 

 

 

  • 트랜잭션을 지원하는 쓰기 지연

1차 캐시와 비슷한 맥락으로 내부에 쓰기 지연 SQL 저장소를 이용해 영속화를 진행하고, 이를 commit()메서드를 호출하면 내부적으로 쓰기 지연 SQL 저장소에서 flush()가 원하는 작업의 쿼리가 DB로 전달된다. (커밋되는 순간 DB에 쿼리 던짐)

 

 

  • 변경 감지 ( Dirty Checking )

JPA는 쓰기 지연과 1차 캐시를 통해 변경과 수정을 감지해주는데, 트랜잭션 되는 순간 내부적으로 flush()가 호출되고,이 때 1차 캐시 내부의 스탭샷(최초 상태)를 비교하는데, 변경이 있으면 UPDATE 쿼리를 쓰기 지연 SQL 저장소에 저장하고,

commit이 되면 flush가 호출되어 DB에 Update 쿼리가 날아간다.

 

 

  • 지연 로딩(Lazy Loading)

연관 관계 매핑이 되어있는 entity 조회 시 프록시를 반환함으로써 쿼리를 진짜 필요할 때 날려준다.


추가사항?

플러시?

영속성 컨텍스트의 변경내용을 데이터베이스에 반영

DB 트랜잭션이 commit되는 시점에서 플러쉬가 발생한다. 

플러시 발생

- 변경감지 (Dirty Checking)

- 수정된 엔티티 쓰지 지연 SQL 장소에 등록

- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송

영속성 컨텍스트를 플러시하는 방법

- em.flush() - 직접 호출 (여기서 em은 entityManager)

- 트랜잭션 커밋 - 플러시 자동 호출

- JPQL 쿼리 실행 - 플러시 자동 호출

플러시는 !

- 영속성 컨텍스트를 비우지 않음

- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화

-트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨


준영속 상태?

* 영속 -> 준영속

* 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)

* 영속성 컨텍스트가 제공하는 기능을 사용하지 못함

준영속 상태로 만드는 방법

- em. detach(entity) => 특정 엔티티만 준영속 상태로 전환 - em.clear() => 영속성 컨텍스트를 완전히 초기화 - em.close() => 영속성 컨텍스트를 종료

 

 

※주의

- 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유

- 엔티티 매니저는 쓰레드간에 공유X(사용하고 버려야 함)

- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행 (정말 중요)

JPQL<< 매우 중요 (모르면 실무 불가)

- JPA를 사용하면 엔티티 객체를 중심으로 개발

- 문제는 검색 쿼리

- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색

- 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능

- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요

-JPA는 SQL을 추상화한 JPQL 이라는 객체 지향 쿼리 언어 제공

- SQL과 문법 유사

(차이점)

- JPQL은 엔티티 객체를 대상으로 쿼리

 

- SQL은 데이터베이스 테이블을 대상으로 쿼리