Persistence Context와 EntityManager

profile image 스이연 2025. 5. 19. 17:21

JPA를 다루면서 기본적으로 알아야하는 지식중 하나가 영속성이다. JPA는 기본적으로 영속성이라는 특징을 갖고 DB와 연결되어 데이터들이 다뤄지는 것으로 알고 있는데, 핵심 개념인 영속성 컨텍스트(Persistence Context)와 엔티티 메니저(EntityManager)에 대해 공부해보려한다.

 

Persistence Context (영속성 컨텍스트)

  • 엔티티를 영구 저장하는 환경
  • 애플리케이션이 데이터베이스에서 꺼내온 객체를 보관하는 역할을 한다.

EntityManager

  • EntityManger를 생성하면 영속성 컨텍스트가 안에 생성이 되는데, 이를 통해 영속성 컨텍스트에 접근 및 관리가 가능하다.
  • EntityMangerFactory를 통해 Entity Manger를 생성할 수 있고 Entity Manger는 내부적으로 Connection을 통해 DB에 접근이 가능하다.

영속성 컨텍스트를 사용하는 이유

1차 캐시

DB를 거치지 않고 1차 캐시에서 바로 사용가능하다는 장점이 있다. 이때 캐시는 Map 형태로 저장된다. key에는 DB의 기본키, value에는 객체를 저장한다.

Member member  = new Member();
member.setId("member1");
member.setUsername("회원");

// 1차 캐시에 저장됨
em.persist(member);

// 1차 캐시에서 조회
Member findMembet = em.find(Member.class, "member1");
엔티티 조회 과정
조회할 엔티티를 1차 캐시에서 찾는다.
→ 1차 캐시에 조회할 엔티티가 있을 경우 DB까지 가지 않고 바로 엔티티를 반환한다.
→ 1차 캐시에 조회할 엔티티가 없을 경우 DB까지 들어가서 조회할 엔티티를 찾는다.

 

동일성 보장

같은 트랜잭션 안에 있는 객체는 == 비교 연산이 보장된다.

 

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

em.persist 명령이 있을 때마다 한 트랜잭션 안에서 사용되는 SQL 문을 쓰기 지연 SQL 저장소에 저장해두고 영속성 컨텍스트를 1차 캐시에 저장한다. transaction.commit 명령이 있을 때 em.flush 이 호출되며, 1차 캐시에 저장되었던 영속성 컨텍스트와 모아두었던 SQL이 한번에 DB로 적용이 된다.

해당 기능은 추후 네트워크 부하를 줄이기 위한 기능으로 사용된다.

 

변경 감지(Dirty Checking)

변경 감지란 영속성 컨텍스트를 DB에 보내기 전 영속성 컨텍스트에 변화가 있었는지 확인 한 후 변화가 있었다면 수정을 한 후 DB에 보내는 작업이다.

transaction.commit 이 발생하면 자동으로 em.flush 가 작동되며 1차 캐시에 저장되었던 영속성 컨텍스트가 DB로 들어가게 되는데, 들어가기전에 1차 캐시 안 엔티티와 스냅샷(맨 처음에 영속성 컨텍스트에 들어온 객체 상태)를 비교한다. 만약 이 둘이 다르다면 SQL 저장소에 update 쿼리문을 추가하고 commit을 한다.

@Transactional(readOnly=true) 설정 관련
해당 설정은 em.flush 가 자동으로 작동되는 것을 막는 설정이다.
이를 설정하는 이유는 오로지 조회용으로 가져온 Entity의 변경 감지로 인한 수정을 막고 snapshot을 따로 보관하지 않음으로써 메모리를 절약하기 위해 설정한다.

 

지연 로딩

접근하려는 엔티티가 실제로 사용될 때 로딩하는 것을 의미한다.

예를 들어 아래와 같은 엔티티 클래스가 존재할 때,

@Entity
public class Student {
	@Id
	private Long id;
	
	@ManyToOne
	private School school;
}

엔티티에 다음과 같이 접근하려고 한다.

Student student = studentRepository.find(studentId); // (1)
School school = student.getSchool(); // (2)
String schoolName = school.getName(); // (3)

(1) 번 시점은 Student 객체를 조회하고 있는 시점인데, 지연 로딩을 사용하게 되면 Student 엔티티 객체가 실제로 사용하고 있는 Student에 대한 SELECT 문을 사용하고 (3) 번 시점에서는 School 에 대한 SELECT 문만을 사용하게 된다.

즉시 로딩이란?
객체를 조회할 때 조회할 객체와 연관된 엔티티들을 모두 한번에 불러오는 것을 의미한다.
예를 들어 (1) 번 시점에서 지연 로딩은 Student 를 SELECT 하는 쿼리문만 사용했다면 즉시 로딩은 Student와 연관된 School를 SELECT 하는 쿼리문까지 한번에 사용한다.

 

EntityManager 라이프사이클

출처: https://jiwondev.tistory.com/225