Cascade 나 OrphanRemoval 영속성 전이를 배우기 전에 꼭 영속성 컨텍스트라는 단어가 나왔다.
Cascade 하고 OrphanRemoval 는 아는데, 대체 영속성 컨텍스트가 뭔지 정확히 알고 싶어졌다.
엔티티 객체를 저장하고 관리하는 메모리 공간, 즉 JPA가 엔티티를 관리하는 1차 캐시이다.
JPA 내부 기능이다.

영속성 컨텍스트는 EntityManager에 의해 관리되며,
하나의 식별자(ID)에 대해 하나의 엔티티 인스턴스만 존재하도록 보장하고,
그 생명주기를 관리하는 메모리 내 저장소이다.
JPA에서 데이터를 DB에서 꺼내오거나 저장할 때,
직접 DB에 접근하는 것이 아니라, 먼저 영속성 컨텍스트에 등록하여 관리된다.
이 컨텍스트에 등록된 객체를 영속 상태라고 하며,
영속성 컨텍스트는 보통 EntityManager 인스턴스당 하나씩 존재한다.
학교 시절을 생각해보자.
출석부(영속성 컨텍스트)에는 모든 학생들(엔티티)의 정보가 적혀 있다.
선생님(EntityManager)은 이 출석부를 가지고 학생들을 관리한다.
만약 "김철수"라는 학생을 처음 본다면,
선생님은 출석부에 새로 등록한다 // em.persist(철수)
이미 출석부에 있는 학생이면,
새로 등록하지 않고 기존 정보를 계속 사용한다 // 동일성(identity) 보장
학생이 지각을 했다면, 선생님은 바로 행정실(DB)에 보고하지 않고
출석부에만 적어두었다가 나중에 한 번에 보고한다 // 변경 감지 + 지연 쓰기
하루 수업이 끝나고 선생님은 출석부를 행정실에 제출한다 // flush / commit
이처럼 출석부는 '현재 반에서 관리 중인 학생들'을 추적하고 기록하는 도구다.
바로 이 역할이 JPA에서의 영속성 컨텍스트다.
기본적으로 총 2개의 범위가 존재한다.
1. 트랜잭션 범위 ( 기본값 )
2. 확장 범위 ( 선택값 )
일반적인 웹/서비스 환경에서는 트랜잭션 범위가 안전하고 효율적이며,
확장 범위는 상태 유지가 필요한 특수한 경우에만 신중하게 사용해야 한다.
트랜잭션이 시작될 때 생성되고, 끝나면 사라진다.
비유: 한 번 수업하고 나면 버려지는 임시 출석부 같은 느낌이다.
- 수업 시작 = 트랜잭션 시작
- 수업 종료 = 트랜잭션 종료 (출석부 폐기)
출석부는 수업 시간 동안만 사용되며,
수업이 끝나면 관리 중이던 학생들도 추적이 끊긴다. // Detached 상태
다음 수업에서는 새로운 출석부가 만들어지고,
같은 학생이라도 새로 기록해야 동일한 추적이 가능하다. // 동일성 보장 안 됨
학생이 수업 중에 정보를 수정하더라도,
선생님은 메모만 해두고 수업 끝날 때 한꺼번에 행정실에 보고한다. // (Dirty Checking + flush)
트랜잭션에서도 그럼 선생님은 항상 같으니까 EntityManager 는 항상 같은 건가?
└ 트랜잭션 범위에서는 EntityManager 가 임시 선생님으로 배정 된다.
즉, 수업을 할 때 마다 다른 선생님이 들어오게 된다.
트랜잭션과 무관하게 EntityManager의 생명주기 동안 유지된다.
비유: 학교에서 상담선생님은 항상 같은 상담기록부를 들고 다닌다. // 동일성 보장
└ 여러 번의 상담(트랜잭션)이 지나도 같은 기록부(영속성 컨텍스트)를 계속 사용한다.
단, 상담선생님이 학교를 그만두게 된다면 상담기록부도 없애버린다.
그리고 다른 상담선생님이 오더라도 이전 기록부는 보지 못한다. // 동일성 보장 안 됨
또한 상담 중 학생 정보가 변경되더라도 선생님은 메모만 해두고,
퇴근(플러시)할 때 한 번에 행정실(DB)에 반영한다. // 변경 감지 + flush
전부 설명하면 길어지고 어려워지니 한번에 설명하도록 하겠다.
실무 측면에서 생각해보면 각각의 영속 상태는 다음과 같은 행동을 취한다.
1. 비영속
└ DB에 저장되지 않았으며, 아직 영속성 컨텍스트에 등록되지 않은 객체다.
└ 주로 new 키워드로 생성한 직후이며, persist()를 호출하기 전 상태다.
└ 실무에서는 입력 폼 데이터를 엔티티로 변환할 때 자주 생성된다.
2. 영속
└ EntityManager 또는 Spring Data JPA에 의해 관리되고 있는 상태다.
└ 트랜잭션 안에서 조회되거나 persist()된 객체가 여기에 해당된다.
└ 실무에서는 조회 후 필드를 수정하면 자동으로 DB에 반영되므로, 매우 많이 활용된다.
3. 준영속
└ 원래는 영속 상태였지만 detach(), clear(), 혹은 트랜잭션 종료로 관리가 끊긴 상태다.
└ 변경해도 자동 반영되지 않으며, 다시 반영하려면 merge()를 호출해야 한다.
└ 실무에서는 배치 처리나 화면에서 객체를 들고 있을 때 발생할 수 있다.
4. 삭제
└ remove() 호출로 삭제 예약된 상태이며, 커밋 시점에 DB에서 실제 삭제된다.
└ 삭제 대상은 반드시 영속 상태여야 하며, 그렇지 않으면 예외가 발생한다.
└ 실무에서는 게시글 삭제, 회원 탈퇴 등의 상황에서 명시적으로 처리된다.
1. 비영속 (Transient)
Student 철수 = new Student("철수");
└ 철수가 입학원서만 작성했을 뿐, 아직 학교에 제출하지 않은 상태.
└ 즉, 학교(영속성 컨텍스트)는 철수를 모름.
2. 영속 (Persistent)
em.persist(철수);
└ 철수가 입학원서를 제출해 학교에 등록됨.
└ 이제부터 학교는 철수의 출결과 성적을 관리하게 됨.
3. 준영속 (Detached)
em.detach(철수);
└ 철수가 휴학을 해서 학교와 연결이 끊긴 상태.
└ 기록은 남아있지만, 학교는 더 이상 출결을 체크하지 않음.
4. 삭제 (Removed)
em.remove(철수);
└ 철수가 자퇴 신청을 함.
└ 학교는 이 정보를 DB에서 완전히 삭제할 예정 (트랜잭션 커밋 시 실제 삭제).
내가 말하는 영속 등록이란,
단순히 객체를 new로 생성한 비영속 상태에서 영속 상태로 전이시키는 걸 말한다.
em.persist 또는 repository.save() 를 사용하면 된다.
EntityManager em;
Member member = new Member("kun"); // 비영속 상태
em.persist(member); // 영속성 컨텍스트에 등록 (영속 상태)
직접 등록해주는 방식
OR
Member member = new Member("kun"); // 비영속 상태
memberRepository.save(member); // 내부적으로 persist() 또는 merge() 호출됨
간접 등록해주는 방식
마찬가지로 JPAQueryFactory 가 entityManager 을 쓴다는 의미로 사용된다.
영속 등록(persist)하려는 게 아니라 단순히 영속성 컨텍스트를 조회 용도로 사용하기 위한 것이에요.
처음에 개념을 접할떄는 굉장히 어려웠다.
하지만 EntityManager가 관리하는 메모리 공간(1차 캐시)이라는 시각으로 바라보면
이해가 한결 쉬워졌다.
다음에는 영속성 전이에 대해서 배워보도록 하겠다.