Spring의 영속성

ParkIsComing·2023년 10월 25일

Spring

목록 보기
15/21
post-thumbnail

다크 모드를 권장합니다

영속성 관리

EntityManagerFactory와 EntityManager

  • 엔티티 매니저는 엔티티 CRUD와 관련된 모든 일을 처리한다.
  • 엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다.
  • 엔티티매니저는 데이터베이스 연결이 필요할 때(예: 트랜잭션 시작)마다 커넥션풀에서 커넥션을 획득한다.

엔티티의 생명주기

  • 영속성 컨텍스트 : 엔티티를 영구 저장하는 환경
  • 엔티티 매니저로 엔티티를 저장/조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관
    • 이때 “엔티티를 저장”한다는 것
      • DB에 곧바로 저장한다 (x)
      • 영속성 컨텍스트에 보관한다(o)
  • 영속성 컨텍스트는 논리적인 개념에 가깝다.

영속성 컨텍스트의 상태

4가지 상태가 존재한다.

  • 비영속 (new/transient)
    : 영속성 컨텍스트와 무관한 상태. 엔티티를 생성하고 아직 저장하지 않았을 경우 비영속에 해당됨.
  • 영속(managed)
    : 엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한 상태. 영속성 컨텍스트에 의해 관리되는 상태.
  • 준영속(detached)
    : 영속성 컨텍스트에 의해 관리되다가 영속성 컨텍스트에서 분리되어 ,더 이상 영속성 컨텍스트에 의해 관리되지 않는 상태
  • 삭제(removed)
    : 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한 상태

영속성 컨텍스트 : 1차 캐시

영속성 컨텍스트는 내부에 1차 캐시를 들고 있다.
em.persist()를 실행하면 엔티티가 영속성 컨텍스트 내부의 1차 캐시에 저장된다. 이때 엔티티는 식별자 값(@Id로 테이블의 PK와 매핑한 값)으로 구분된다.

영속성 컨텍스트 : 엔티티 조회

영속성 컨텍스트 : 동일성 보장

영속성 컨텍스트 : 쓰기 지연

영속성 컨텍스트의 이점 : 엔티티 등록

  1. persist()로 엔티티를 영속화
  2. 엔티티는 영속성 컨텍스트 1차 캐시에 저장 && JPA가 엔티티를 분석해 INSERT 쿼리를 생성하여 쓰기 지연 SQL 저장소에 쌓아둠
    -> transaction.commit();
  3. 트랜잭션을 커밋하면 쓰기 지연 SQL 저장소에 있는 쿼리들이 flush 되면서 DB에 날아간다.
  4. DB 트랜잭션 커밋.

영속성 컨텍스트의 이점 : 엔티티 수정, 변경 감지

  • 변경 감지 : 엔티티의 변경사항을 DB에 자동으로 반영하는 기능
  1. 트랜잭션을 커밋
  2. 영속성 컨텍스트 내부에서 flush 호출
  3. 엔티티랑 스냅샷을 비교하여 변경된 엔티티 찾음
    *스냅샷 : 1차 캐시 안에 들어오는 최초의 상태를 본떠둔 것
  4. 변경된 것은 UPDATE 쿼리를 생성하여 쓰기 지연 SQL 저장소에 보냄
  5. 쓰기 지연 저장소의 SQL을 DB에 보냄
  6. DB 트랜잭션 커밋.

영속성 컨텍스트의 이점 : 엔티티 삭제

  1. em.remove(memberA)로 삭제할 엔티티를 넘겨주면 DELETE 쿼리를 쓰기 지연 SQL 저장소에 등록
    -> 이때 memberA 엔티티는 영속성 컨텍스트에서 제거됨
  2. 트랜잭션을 커밋하면 flush 호출하여 DB에 DELETE 쿼리 전달

플러시

  • 플러시 : 영속성 컨텍스트의 변경 내용을 DB에 반영하는 것

플러시를 실행하면

  1. 변경 감지가 동작하여 영속성 컨텍스트에 있는 모든 엔티티와 스냅샷을 비교하여 수정된 엔티티를 찾음
  2. 수정된 엔티티는 UPDATE 쿼리를 만들어 쓰기 지연 SQL 저장소에 쌓아둠
  3. 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송

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

  1. em.flush() 메서드 호출
    : 테스트나 다른 프레임워크와 JPA를 같이 사용할 때를 제외하고는 거의 사용하지 않는다.
  2. 트랜잭션 커밋 시 플러시 자동 호출
    : JPA는 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
  3. JPQL 쿼리 실행시 플러시 자동 호출
    : JPA는 JPQL을 실행할 때도 플러시를 자동으로 호출한다.

즉시 로딩과 지연 로딩

1. 즉시 로딩

엔티티를 조회할 때 연관된 엔티티도 함께 조회
@ManyToOne(fetch = FetchType.EAGER)로 설정

2. 지연 로딩

연관된 엔티티까지 함께 조회할 필요는 없을 때 사용
@ManyToOne(fetch = FetchType .LAZY)로 설정한다.

@ManyToOne, @OneToOne과 같이 @XXXToOne 어노테이션들은 기본이 즉시 로딩(EAGER) 이다.

N+1 문제


1. findAll()을 한 순간 select t from Team t 이라는 JPQL 구문이 생성
2. 해당 구문을 분석한 select from team 이라는 SQL이 생성되어 실행된다.
3. DB의 결과를 받아 team 엔티티의 인스턴스들을 생성한다.
4. team과 연관되어 있는 user 도 로딩을 해야 한다.
5. 영속성 컨텍스트에서 연관된 user가 있는지 확인한다.
6. 영속성 컨텍스트에 없다면 3번에서 만들어진 team 인스턴스들 개수에 맞게 select
from user where team_id = ? 이라는 SQL 구문이 생성된다. ( N+1 발생 )

0개의 댓글