영속성 컨텍스트

이건우·2025년 3월 12일

웹 프로그래밍

목록 보기
19/43

기능 및 특징

1. 엔티티 관리 (Entity Management)

엔티티의 생명주기 관리 (영속, 비영속, 준영속, 삭제 상태 등)

2. 1차 캐시(First Level Cache)

엔티티 매니저(EntityManager)가 관리하는 내부 캐시로, 조회된 엔티티를 메모리에 저장.
동일 트랜잭션 내에서는 동일한 엔티티를 재조회 시 DB 접근 없이 캐시에서 가져옴.

3. 동일성(identity)보장

동일 트랜잭션 내에서 같은 엔티티를 항상 동일한 인스턴스로 보장.

4. 더티 체킹(Dirty Checking)

엔티티의 변경 사항을 자동으로 찾아내 업데이트 SQL을 생성.

5. 지연 쓰기(Write-behind)

변경사항을 즉시 DB에 반영하지 않고, 트랜잭션 커밋 시에 한 번에 일괄적으로 반영.


영속성 컨텍스트 주요 메소드

  • persist(entity): 엔티티를 영속성 컨텍스트에 저장
  • find(entityClass, primaryKey): 기본 키로 엔티티 조회
  • remove(entity): 엔티티 삭제
  • detach(entity): 특정 엔티티를 준영속 상태로 변경
  • clear(): 영속성 컨텍스트를 초기화 (모든 엔티티 준영속화)
  • flush(): 영속성 컨텍스트의 변경 내용을 즉시 데이터베이스에 반영 (커밋은 안됨)

영속성 컨텍스트의 생명주기 상태

엔티티는 영속성 컨텍스트와의 관계에 따라 4가지 상태로 구분됩니다.

비영속 (Transient)

영속성 컨텍스트가 관리하지 않는 상태
ex) new Member() 객체 생성 직후 상태

영속 (Managed)

영속성 컨텍스트가 관리하는 상태
데이터베이스에 저장된 상태이거나, 저장될 예정인 상태
ex) em.persist(member) 호출 후 상태

준영속 (Detached)

영속성 컨텍스트에서 관리하던 엔티티가 더 이상 관리되지 않는 상태
ex) em.detach(member), em.clear(), em.close() 호출 후 상태

삭제 (Removed)

영속성 컨텍스트에서 관리하던 엔티티를 데이터베이스에서도 삭제할 예정인 상태
ex) em.remove(member) 호출 후 상태

++ JPA Repository

JPA Repository를 사용할 때는 영속성 컨텍스트가 자동으로 상태 관리를 해줍니다. 즉, 개발자가 직접 EntityManager를 다루지 않아도, JPA Repository에서 제공하는 메소드를 통해 엔티티가 자동으로 영속성 컨텍스트의 관리 상태로 들어가게 됩니다.

JPA Repository의 수행과정

  1. JPA Repository 메소드를 호출하면 스프링은 내부적으로 트랜잭션을 열고, 자동으로 엔티티를 영속성 컨텍스트에 저장(영속화) 합니다.

  2. 조회된 엔티티는 자동으로 영속 상태(managed) 로 관리됩니다.

  3. 트랜잭션 종료 시, 영속성 컨텍스트의 더티 체킹(Dirty Checking) 기능에 의해 변경된 엔티티의 상태가 자동으로 데이터베이스에 반영됩니다.

즉, 직접 persist(), merge(), remove() 등을 호출하지 않아도 자동으로 처리됩니다.
그러나, 이러한 과정들은 영속성 컨텍스트의 트랜잭션 내에서 이루워 져야 하기에,
직접 영속성 컨텍스트의 트랜잭션의 범위를 지정하거나, @Transactional으로
특정 메소드 전체를 범위로 지정 할 수 있습니다.

잘못된 예시 (트랜잭션 X)

public void updateWithoutTransaction(Long id, String name) {
    Optional<Member> optionalMember = memberRepository.findById(id);
    if(optionalMember.isPresent()){
        Member member = optionalMember.get();
        member.setName(name);
        // DB에 반영되지 않음! 트랜잭션이 없기 때문
    }
}

올바른 예시 (트랜잭션 O)

@Transactional
public void updateWithTransaction(Long id, String name) {
    Optional<Member> optionalMember = memberRepository.findById(id);
    if(optionalMember.isPresent()){
        Member member = optionalMember.get();
        member.setName(name);
        // 트랜잭션 종료 시 DB 자동 반영
    }
}
profile
새싹개발자

0개의 댓글