@Transient 어노테이션과 영속성 컨텍스트 (1) - 이론편

조갱·2023년 11월 11일
0

실무에서 Entity를 관리하면서 @Transient 어노테이션을 사용할 일이 생겼다.
대충 db 컬럼에서 제외한다는건 알고있었는데, 제대로된 개념을 학습하기 위해 인터넷 검색을 하던 중 다음 포스팅(JPA에서 @Transient 애노테이션이 존재하는 이유)을 보게된다.

포스팅 본문 중간에 단순히 “컬럼을 제외한다.” 라기보단 영속 대상에서 제외시키기 위해 사용한다고 이해하셔야 합니다.라는 구문이 있는데, 딱 내가 생각하던 컬럼을 제외 가 아닌 영속 대상에서 제외한다고 이해해야 한다고 한다 ㅎㅎ,,,

JPA (Java Persistence API)를 사용하면서, 늘 영속성컨텍스트라는 단어를 들어왔는데
아직도 영속성이 어떤 뜻인지 잘 모르겠다. (그래서 위 포스팅에서 Transient가 필드를 영속 대상에서 제외시킨다는 의미도 잘 이해하지 못했다.)

그래서 이번 포스팅에서는 영속성의 의미, 영속성 컨텍스트와 Transient어노테이션에 대해 학습해보고자 한다.

영속성

데이터가 영구적으로 보관되는 환경

일반적으로 데이터는 메모리로, 휘발성의 특징을 띄고 있다.
즉, 프로그램이 종료되거나 PC가 종료되면 데이터가 소멸됨을 의미하기도 한다.

이를 영구적으로 저장하기 위해서는 파일 형태로 관리를 해야하는데,
파일 형태로 관리하기 위한 대표적인 방법은 DB이다.

영속성 컨텍스트는 파일(DB)와, 메모리(Entity) 사이에서
데이터가 영구적으로 저장될 수 있는 중간 다리 역할을 한다.

영속성 컨텍스트

파일(DB)와 메모리(Entity) 사이에서 데이터가 영구적으로 저장될 수 있는 환경을 관리

생명주기

인터넷에서 영속성 컨텍스트를 검색하면 99.99% 항상 보이는 이미지이다.

상태

JPA의 영속에는 총 4가지 상태가 있다.

  • 비영속 (New, Transient)
    데이터가 아직 영속성 컨텍스트에 영속 대상으로 등록되지 않은 상태이다.
    단순히 New 키워드를 통해 Entity 객체를 생성한 상태이며,
    이때는 Entity의 값을 바꿔도 업데이트 (더티체킹: 아래에서 다시 설명) 되지 않는다.
  • 영속 (Managed)
    영속성 컨텍스트에서 영속 대상으로 관리하고 있는 객체이다.
    데이터를 읽을 때마다 1차 캐시에 저장이 되어, 캐싱 기능을 지원한다.
    Entity의 필드 값이 변경된다고 즉시 DB에 반영되는것이 아니고, 트랜잭션이 commit 되는 시점에 변경된 내용을 감지하여 DB에 쿼리가 반영된다.
  • 준영속 (Detached)
    일시적으로 영속 대상에서 제외한 상태이다.
    영속성 컨텍스트가 제공하는 더티체킹, Update Query 등의 기능은 사용하지 못한다.
  • 삭제 (Removed)
    Entity가 DB에서 제외되는 상태이다.
    Remove상태가 된다고 즉시 DB에서 지워지는건 아니고, flush()가 수행돼야 DB에 반영된다.

1차 캐싱

영속성 컨텍스트로 관리되는 Entity들은 @Id 어노테이션이 붙은,
즉 PK로 관리되는 필드를 키값으로 캐싱이 된다.

캐싱이 되는 시점은 아래와 같다.

  • Entity를 영속 대상으로 등록할 때
    • EntityManager에 persist(...) -> 1차 캐시에 캐싱
  • 데이터를 DB에서 조회할 때
    • 1차 캐시에서 데이터를 조회 -> (없으면) DB에서 데이터 조회 -> 1차 캐시에 캐싱

객체를 캐싱하여 가지고 있기 때문에, 캐시에서 조회된 데이터는 모두 동일한 메모리 주소를 갖는다. 즉, 같은 객체이다.

Member a = em.find(Member.class, "Member1");
Member b = em.find(Member.class, "Member1");
Systen.out.println(a == b); // 참조값이 같음

JPA는 1차 캐싱을 통해 REPEATABLE_READ 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 어플리케이션 레벨에서 제공한다. : 관련 포스팅

1차 캐싱을 사용하면서 주의해야할 점은, PK로 검색할 때만 1차 캐싱이 지원된다.
즉, Spring-Data-JPA에서 findById 로 찾은 값만 캐싱이 된다.
그 외에 findByXX 로 찾은 값은 캐싱이 되지 않는다 : 관련 포스팅

flush

flush는 영속성 컨텍스트에서 관리하는 Entity를 DB에 반영 하는 기능이다.

flush가 발생하는 시점은 아래와 같다.

  • EntityManager의 flush()를 명시적으로 호출할 때
  • commit() 이 발생하기 직전

flush는 영속 대상을 영속성 컨텍스트나 1차 캐시에서 제외시키는 것이 아니라,
DB에 동기화시키는 과정임에 유의해야한다.

또한, flush() 를 수행하면 INSERT/UPDATE/DELETE 쿼리가 실행되긴 하지만, 그 즉시 DB에 반영되는 것은 아니다. 실제 DB에 반영 되는 시점은 commit() 이 수행될 때이다. (RDB 기본 개념)

당연하게도 commit을 하기 위해서라면, 이러한 수정사항들은 transaction 내에서 수행되어야 한다.

EntityManager em = ...
em.getTransaction().begin();

// 엔터티를 영속성 컨텍스트에 추가 또는 조회 및 수정 등의 작업 수행

em.getTransaction().commit();
// 트랜잭션이 종료되면 영속성 컨텍스트에 있는 엔터티는 detached 상태가 될 수도 있음 (아래서 별도 설명)

Dirty Check

Entity에 수정사항이 발생한다고, 그 즉시 update 쿼리가 발생하는 것은 아니다.
트랜잭션이 끝나는 시점에 1차 캐시와 비교하여 변경 사항이 있으면 update 쿼리가 실행된다.

이것을 더티 체크, 지연 쓰기라고 하며, 더티 체크를 사용함으로써 불필요한 쿼리를 줄임으로써 DB 부하를 막을 수 있다. (영속 컨텍스트의 장점이기도 하다.)

참고로, update 쿼리가 수행될 때는 Entity에 존재하는 모든 필드가 update 쿼리에 포함된다.
완전히 수정된 필드만을 update 쿼리에 포함하기 위해서는 @DynamicUpdate어노테이션을 클래스단에 적용하면 된다.

EntityManager

EntityManagerFactory

EntityManagerFactory는 이름 그대로, EntityManager를 생성하는 역할을 한다.

EntityManagerFactory는 생성 비용이 비싸지만, Thread-safe 한 특징을 가진다.
따라서 어플리케이션이 시작되는 최초 1회에 한해 생성되며, 이후에 EntityManager를 생성할 때는 이 Factory를 재사용한다.

EntityManager

EntityManager는 영속성 컨텍스트를 생성하고 이를 관리하는 주체이며, 영속성 컨텍스트는 Entity의 상태를 추적하고 Entity 매니징과 관련된 여러 기능을 제공하는 논리적인 단위이다.

EntityManager는 생성하는 비용이 저렴한 특징을 가지며, Thread-Safe 하지 않아서 매 요청마다 새로운 EntityManager를 생성해야한다.

EntityManager는 인터페이스이므로, 구현체마다 그 속성과 동작 방식이 다르다.

JPA에서 사용하는 Hibernate 구현체에서도 이 EntityManager를 사용한다.
다음 시간에는 하이버네이트에서 구현하는 EntityManager인 SessionImpl에 대해 살펴보겠다.

(참고로 Session은 org.hibernate 패키지에 있고, SessionImpl은 패키지에 위치한다.)

다시 돌아와서

이렇게 영속성에 대한 개념을 정리하고 나니,
처음에 궁금했던 @Transient 어노테이션이 단순히 “컬럼을 제외한다.” 라기보단 영속 대상에서 제외시키기 위해 사용한다고 이해하셔야 합니다. 에 대한 해답을 얻을 수 있었다.

@Transient 어노테이션을 적용하면 ->
Entity매니저가 해당 필드를 영속 대상으로 관리하지 않음 ->
Entity에 변화가 일어나도 UPDATE 쿼리에 포함되지 않음 ->
DB 업데이트 대상 컬럼에서 제외

Reference
1차 캐싱 REPEATABLE_READ 트랜잭션 격리 수준 실험
https://techvu.dev/116
1차 캐싱 findByXXX 에 따른 SELECT 쿼리 수행 횟수 실험
https://devoong2.tistory.com/entry/JPA-영속성-컨텍스트Persistence-Context의-5가지-특징
Transient 어노테이션
https://gmoon92.github.io/jpa/2019/09/29/what-is-the-transient-annotation-used-for-in-jpa.html
EntityManager와 영속성 컨텍스트
https://siyoon210.tistory.com/138\
그 외에 영속성 컨텍스트 관련 참고한 글
https://sugerent.tistory.com/587
https://velog.io/@devtel/JPA-%EC%98%81%EC%86%8D%EC%84%B1persistence%EC%9D%B4%EB%9E%80
https://hyeooona825.tistory.com/87
https://velog.io/@neptunes032/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80
https://binco.tistory.com/entry/JPA-%EC%98%81%EC%86%8D%EC%84%B1%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%A0%95%EC%9D%98-%ED%95%B5%EC%8B%AC%EC%9A%94%EC%95%BD
https://ittrue.tistory.com/254
https://devoong2.tistory.com/entry/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-Context%EC%9D%98-5%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%A7%95
https://ict-nroo.tistory.com/130
https://jojoldu.tistory.com/415

profile
A fast learner.

0개의 댓글