영속성 컨텍스트(Persistence Context)란?
영속성 컨텍스트(Persistence Context)는 JPA(Java Persistence API)에서 엔티티(Entity) 객체를 관리하는 캐시 같은 메모리 공간입니다. 영속성 컨텍스트는 데이터베이스와 애플리케이션 간의 중간 계층 역할을 하며, 엔티티 객체를 관리하고 데이터베이스와 동기화합니다.
엔티티는 영속성 컨텍스트에서 다음과 같은 상태를 가질 수 있습니다:
Member member = new Member(); // 비영속 상태
em.persist(member); // 영속 상태로 전환
em.detach(member); // 준영속 상태
em.remove(member); // 삭제 상태
영속성 컨텍스트는 1차 캐시를 이용하여 동일한 엔티티를 한 번만 데이터베이스에서 조회합니다.
동일한 트랜잭션 내에서 find()로 같은 ID의 엔티티를 두 번 조회하면, 두 번째는 데이터베이스를 조회하지 않고 1차 캐시에서 반환합니다.
Member member1 = em.find(Member.class, 1L); // DB에서 조회 후 1차 캐시에 저장
Member member2 = em.find(Member.class, 1L); // 1차 캐시에서 반환
System.out.println(member1 == member2); // true (동일 객체)
영속성 컨텍스트는 엔티티의 변경 사항을 추적하여, 트랜잭션 커밋 시 자동으로 변경 내용을 데이터베이스에 반영합니다. persist()를 호출하지 않아도, set 메서드를 통해 값이 변경되면 자동으로 업데이트 쿼리를 생성합니다.
Member member = em.find(Member.class, 1L);
member.setUserName("updatedName"); // 변경 감지
// 트랜잭션 커밋 시 UPDATE 쿼리 실행
영속성 컨텍스트는 트랜잭션 커밋 전까지 SQL 쿼리를 데이터베이스에 전송하지 않고 모아둡니다.
트랜잭션이 종료되면 한 번에 데이터베이스로 쿼리를 전송하여 성능을 최적화합니다.
em.persist(member1); // INSERT 쿼리 생성 (DB로 전송 X)
em.persist(member2); // INSERT 쿼리 생성 (DB로 전송 X)
em.flush(); // 쿼리 전송
영속성 컨텍스트는 필요할 때만 데이터를 로딩하는 지연 로딩(Lazy Loading)을 지원합니다. @OneToMany, @ManyToOne 등 연관된 엔티티를 사용할 때, 실제로 데이터가 필요할 때만 쿼리를 실행합니다. LazyInitializationException은 영속성 컨텍스트가 닫힌 상태에서 지연 로딩을 시도할 때 발생합니다.
성능 최적화
1차 캐시를 통해 중복 쿼리를 방지하여 데이터베이스 접근을 줄임.
쓰기 지연을 통해 다수의 쿼리를 한 번에 처리하여 성능을 개선.
변경 감지와 자동 동기화
엔티티의 변경 사항을 자동으로 감지하고, 데이터베이스에 반영.
개발자가 직접 SQL을 작성하지 않아도 되는 장점.
트랜잭션 관리
동일한 트랜잭션 내에서 데이터 일관성을 보장.
트랜잭션 시작 → 영속성 컨텍스트 생성.
엔티티 저장 → 영속성 컨텍스트가 엔티티를 관리.
엔티티 변경 → 변경 감지로 변경 사항 추적.
트랜잭션 커밋 → 쓰기 지연된 쿼리 실행 및 동기화.
트랜잭션 종료 → 영속성 컨텍스트 종료 및 1차 캐시 초기화.