영속성 컨텍스트

Jiwon Jung·2025년 11월 5일

스프링(Spring)

목록 보기
9/20

이번 포스팅에서는 영속성 컨텍스트에 대해 기록한다.


💡 영속성 컨텍스트란?

  • JPA가 DB와 직접 연결하지 않고 엔티티 객체를 관리하는 메모리 공간이다.
  • 즉, JPA를 통해 엔티티를 저장하거나 조회하면 이 객체는 영속성 컨텍스트에 보관된다.
  • DB랑 직접 연결해서 쓰면 매번 SQL을 실행해야 하지만, JPA는 영속성 컨텍스트라는 임시 저장소를 이용해서 객체를 메모리에 올려두고 관리할 수 있다.

JPA는 엔티티를 직접 DB에 저장하지 않고, 영속성 컨텍스트에 먼저 보관한다.
필요하면 영속성 컨텍스트에서 꺼내 쓰고, 수정된 내용은 자동으로 DB에 반영된다.


💡 영속성 컨텍스트의 주요 특징

1️⃣ 1차 캐시

  • 영속성 컨텍스트는 엔티티와 식별자를 Map 형태로 저장하는 1차 캐시를 내장하고 있다.
  • 동일 트랜잭션 내에서 같은 엔티티를 조회할 때, DB 쿼리 없이 1차 캐시에서 바로 데이터를 반환하여 성능을 향상시킨다.
  • 엔티티가 1차 캐시에 저장되어 있기 때문에, 같은 식별자(@Id)에 대한 엔티티는 매번 같은 인스턴스에 접근할 수 있다.

2️⃣ 쓰기 지연

  • 엔티티에 대한 persist()와 같은 작업은 즉시 DB에 반영되지 않고, 쓰기 지연 저장소에 쿼리를 모아둔다. 이 쿼리들은 트랜잭션이 커밋될 때 한 번에 DB로 전송되어 불필요한 쿼리 실행을 줄여준다.

3️⃣ 변경 감지(Dirty Checking)

  • 영속성 컨텍스트는 엔티티의 스냅샷을 저장해 둔다.
  • 트랜잭션 커밋 시점에 현재 엔티티와 스냅샷을 비교하여 변경된 부분을 감지하고, 해당 부분에 대한 UPDATE 쿼리를 자동으로 생성하여 쓰기 지연 저장소에 추가한다.

4️⃣ 지연 로딩(Lazy Loading)

  • JPA에서는 데이터를 조회할 때, 즉시 로딩(EAGER)과 지연 로딩(LAZY) 두 가지 방식이 있다.
  • 즉시 로딩(EAGER) 방식은 데이터를 조회할 때 연관된 데이터까지 한 번에 불러오는 것이다.
  • 지연 로딩(LAZY) 방식은 필요한 시점에 연관된 데이터를 불러오는 것이라고 할 수 있다.

💡 EntityManager란?

  • EntityManager는 JPA에서 데이터베이스와 소통하는 핵심 객체이다.
  • 영속성 컨텍스트를 관리하고, 엔티티를 저장, 조회, 수정, 삭제하는 역할을 한다.
  • 쉽게 말하면 EntityManager는 JPA의 비서같은 존재이다.

1️⃣ persist()를 사용하여 엔티티 저장(INSERT)

  • persist()를 호출하면 DB에 즉시 저장되지 않고, 영속성 컨텍스트에 먼저 저장된다.
  • 트랜잭션이 끝나면 JPA가 자동으로 DB에 INSERT SQL을 실행한다.
Member member = new Member();
member.setName("JPA");

em.persist(member); // 영속성 컨텍스트에 등록

2️⃣ find()를 사용하여 엔티티 조회(SELECT)

  • 먼저 영속성 컨텍스트에서 찾고, 없으면 그제서야 DB에서 가져온다.
  • 같은 id의 엔티티를 여러 번 조회 시, DB를 다시 조회하지 않고 같은 객체를 반환한다.
@Transactional
public void findUser() {
	User user = entityManager.find(User.class, 1L); // id가 1인 User 조회
    System.out.println(user.getName());
}

3️⃣ 엔티티 값을 변경하면 자동 반영(UPDATE)

  • JPA는 변경 감지(Dirty Checking) 기능이 있어서 setName() 등으로 값을 바꾸면 자동으로 UPDATE SQL을 실행한다.
  • persist() 없이 단순히 엔티티 값만 변경해도 자동 저장된다. (트랜잭션 종료 시 flush())
@Transactional
public void updateUser() {
	User user = entityManager.find(User.class, 1L); // 영속 상태
    user.setName("Java"); // 값 변경
    
    // flush() 자동 실행: 변경된 내용이 DB에 UPDATE
}

4️⃣ remove()를 사용하여 엔티티 삭제(DELETE)

  • remove()를 호출하면 영속성 컨텍스트에서 제거된다.
  • 트랜잭션이 끝나면 자동으로 DELETE SQL이 실행된다.
@Transactional
public void deleteUser() {
	User user = entityManager.find(User.class, 1L); // 조회
    entityManger.remove(user); // 삭제
    
    // 트랜잭션이 끝나면 DELETE SQL 실행
}

5️⃣ detach()를 사용하여 영속성 컨텍스트에서 분리

  • detach()를 호출하면 JPA가 이 객체를 관리하지 않는다.
  • 이후 변경해도 DB에 반영되지 않는다. (변경 감지 x)
@Transactional
public void detachUser() {
	User user = entityManager.find(User.class, 1L); // 조회 (영속 상태)
    entityManger.datach(user); // 영속성 컨텍스트에서 분리
    
    user.setName("Postman"); // 변경했지만 DB에 반영되지 않음
}

0개의 댓글