[JPA #1] JPA란 무엇인가? 영속성 컨텍스트의 개념
주요 키워드
- JPA와 Hibernate
- 영속성 컨텍스트
- 엔티티의 생명주기
- 플러시
서론
이전 회사에 재직중일 때에는 ORM을 사용하지 않았습니다. 당시에도 Ktor에서 사용할 수 있는 Ktorm이나 exposed와 같은 ORM이 존재하고 있었으나, 기존에 시스템이 이미 구현이 되어있는 상태에서 변경해야하는 범위가 너무 컸고, 기존 시스템이 네이티브 쿼리를 사용하고 있지만 엔티티 단위로 사용하자는 원칙에 맞춰 구현되어 있었기 때문에 아직 이해도가 부족한 상태에서 굳이 바꿔야하나라는 생각이 들었습니다. 불편하다고 느꼈던 점은 수정시 변경해야하는 코드의 양이 많고, 오타를 컴파일 시점에 잡아줄 수 없다는 점 정도가 있었는데, 이번 기회에 JPA에 대해 알아보게 되어 흥미로웠습니다.
왜 JPA를 사용하는가?
JPA(Java Persistence API)는 자바 진영의 ORM(Object-Relational Mapping) 표준입니다.
ORM은 객체와 관계형 데이터베이스의 불일치를 해결해주는 기술로,
JPA는 이러한 ORM을 자바 생태계에서 사용할 수 있도록 추상화한 인터페이스입니다.
- SQL 중심 개발 → 객체 중심 개발
- 반복되는 SQL, 매핑 코드 → 선언형 프로그래밍으로 생산성 향상
- 유지보수성, 테스트 용이성 증가
☑️ Hibernate?
JPA는 명세(즉, 인터페이스)이고, 이를 구현한 대표적인 구현체가 Hibernate입니다.
즉, JPA를 사용한다고 하면, 실제 동작은 Hibernate가 처리합니다.
JPA의 구현체로 Hibernate, EclipseLink, DataNucleus 등의 다양한 ORM 프레임워크들이 존재합니다.
영속성 컨텍스트(Persistence Context)란?
JPA의 핵심 개념 중 하나는 바로 영속성 컨텍스트입니다.
이는 엔티티를 영구 저장하거나 추적하는 일종의 메모리 상 저장소라고 볼 수 있습니다.
구성요소
EntityManagerFactory
: 요청 시EntityManager
를 생성EntityManager
: DB와 커넥션을 맺고 영속성 컨텍스트를 관리
요청이 들어올 때마다 EntityManagerFactory
에서 EntityManager
가 생성되고,
각 매니저는 DB와 연결된 고유의 영속성 컨텍스트를 가집니다.
(Spring 환경에서는 N:1 구조로 관리됨)
엔티티의 생명주기
상태 | 설명 | 예시 |
---|---|---|
비영속 (new) | 영속성 컨텍스트와 무관한 상태 | new Member() |
영속 (managed) | 영속성 컨텍스트가 관리하는 상태 | em.persist(member) |
준영속 (detached) | 컨텍스트에서 분리된 상태 | em.detach(member) |
삭제 (removed) | 삭제 대상으로 분류된 상태 | em.remove(member) |
영속성 컨텍스트의 이점
1. 1차 캐시
- 같은 엔티티를 여러 번 조회해도 DB를 다시 조회하지 않고 1차 캐시에서 조회
2. 동일성 보장 (Identity Guarantee)
- 같은 트랜잭션 내에서는 같은 객체 인스턴스 반환
→a == b
→ true
3. 트랜잭션을 지원하는 쓰기 지연 (Write-behind)
persist()
해도 즉시 insert 쿼리 실행 X- 커밋 시점에 모아둔 SQL을 한꺼번에 실행
- Hibernate의의
batch_size
설정으로 버퍼 사이즈 조절 가능
4. 변경 감지 (Dirty Checking)
- 커밋 시
flush()
발생 → 엔티티와 스냅샷을 비교 - 변경된 경우
UPDATE
쿼리를 쓰기 지연 SQL 저장소에 등록
플러시(Flush)란?
- 변경 감지된 엔티티들을 DB에 동기화
등록/수정/삭제
쿼리를 DB에 전송- 플러시 후에도 영속성 컨텍스트는 유지됨(쓰기 지연 쿼리만 날아감)
플러시 발생 시점
- 트랜잭션
commit()
- JPQL 실행 시
em.flush()
수동 호출
준영속 상태
- 영속 상태에서 영속성 컨텍스트에서 분리된 상태
- DB와 동기화되지 않으며 변경 감지도 되지 않음
메서드 | 설명 |
---|---|
em.detach(entity) | 특정 엔티티만 준영속으로 변환 |
em.clear() | 전체 컨텍스트 초기화 |
em.close() | 컨텍스트 종료 |