영속성 컨텍스트
엔티티 매니저로 엔티티를 저장하거나 조회 시 엔티티를 영구적으로 보관하고 관리하는 저장소 같은 개념
엔티티 매니저를 생성할 때 한 개의 영속성 컨텍스트가 만들어지고
엔티티 매니저는 이런 영속성 컨텍스트에 접근하고 관리하는 도구라고 볼 수 있음
em.persist(member);
persist 메서드는 정확하게는 엔티티를 저장하는 메서드가 아닌 영속성 컨텍스트에 저장하는 메서드인 것
엔티티의 생명주기
비영속
영속성 컨텍스트와 아무런 관계가 아닌 상태
엔티티 객체를 생성만 한 상태
영속
영속성 컨텍스트에 저장된 상태
엔티티 매니저를 통해 영속성 컨텍스트에 저장된 상태로
즉, persist 메서드를 사용한 상태
영속성 컨텍스트에 의해 관리가 되고 있는 상태
준영속
영속 상태였다가 분리된 상태
// 영속성 컨텍스트에서 분리
em.detach(member);
// 자동으로 영속성 컨텍스트에서 분리되는 경우
em.close(); // 영속성 컨텍스트 닫기
em.clear(); // 영속성 컨텍스트 초기화
영속성 컨텍스트에 의해 관리가 되고 있지 않는 상태
삭제
엔티티가 영속성 컨텍스트와 데이터베이스에서 삭제된 상태
em.remove(member);
영속성 컨텍스트의 특징
- 엔티티를 식별자 값(@Id 어노테이션으로 매핑된 기본키 컬럼)으로 구분하기 때문에 영속 상태는 식별자 값이 반드시 있어야 하고 없을시 예외 발생
- 트랜잭션이 커밋되는 순간 영속성 컨텍스트의 엔티티들이 데이터베이스에 반영 (flush)
- 영속 상태의 엔티티가 저장되는 1차 캐시라는 내부의 캐시를 가지고 있음
- 엔티티의 동일성을 보장 (같은 엔티티(식별자가 같은)인 경우 주소가 같음)
영속성 컨텍스트의 엔티티 조회
비영속 상태의 엔티티를 영속성 컨텍스트에 영속 시키면
해당 엔티티의 식별자 값과 엔티티를 1차 캐시에 저장
즉, 1차 캐시의 키는 식별자 값이므로 영속성 컨텍스트의 모든 CRUD 작업의 기준은 기본키 값이다.
1차 캐시에서 식별자 값으로 엔티티를 조회한 후에 찾는 엔티티가 없는 경우 데이터베이스에서 조회
find(엔티티클래스, 기본키)
데이터베이스에서 엔티티를 조회한 경우
조회한 엔티티를 생성한 후 1차 캐시에 저장하여 영속 상태의 엔티티를 반환
엔티티 등록 - 쓰기 지연
엔티티 매니저는 트랜잭셕이 커밋되기 전까지 SQL을 내부 쿼리 저장소에 저장해 두고
트랜잭션이 커밋되는 순간 저장해 둔 SQL을 데이터베이스에 전달
- 트랜잭션 시작
- SQL 작업 수행
- SQL 작업 저장
- 트랜잭션 커밋
- 영속성 컨텍스트의 작업 내용을 DB와 동기화하는 flush 실행
- 실제 데이터베이스의 트랜잭션을 커밋
엔티티 수정 - 변경 감지
UPDATE 쿼리문을 일일히 작성할 필요 없이
변경 사항을 데이터베이스에 자동반영해주는 변경 감지 기능이 있기 때문에
수정하고자 하는 엔티티를 조회해서 Setter 메서드 등을 통해 값을 수정하기만 하면 됨
JPA의 변경 감지 기능은 엔티티의 영속성 컨텍스트 보관 시의 상태를 저장해두는 스냅샷과
플러시 시점의 엔티티를 비교해주는 기능이다.
- 트랜잭션 커밋 시 엔티티 매니저 내부에서 플러시 호출
- 현재 엔티티와 스냅샷을 비교하여 변경 사항 조회
- 변경된 엔티티 존재 시 수정 쿼리를 생성해서 쓰기 지연 저장소에 전달
- 쓰기 지연 저장소의 SQL문을 데이터베이스에 전달
- 실제 데이터베이스의 트랜잭션을 커밋
영속 상태의 엔티티에만 적용되는 기능
엔티티 삭제
- 삭제할 엔티티 조회
- remove 메서드를 통해 엔티티 삭제 SQL 저장
플러시(flush)
플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 역할을 수행하는
즉, 작업한 쿼리 내용을 데이터베이스에 최종적으로 저장하는 역할이다.
- 변경 감지를 통해 영속성 컨텍스트에 있는 모든 엔티티를 기존 스냅샷과 현재 상태와 비교
- 수정 된 엔티티들은 쓰기 지연 저장소에 쿼리를 저장
- 데이터베이스에 저장소의 쿼리를 전달
플러시는 위의 과정을 통해 이루어진다.
영속성 컨텍스트의 플러시는 트랜잭션을 커밋하거나 객체지향 쿼리 실행 시 자동으로 호출되며
엔티티 매니저의 flush 메서드를 통해 직접 호출할 수도 있다.
tx.commit();
트랜잭션을 커밋할 때 변경사항(SQL)을 데이터베이스에 전달하지 않으면
데이터베이스는 어떤 변경사항이 있는지 모르기 때문에 반영이 되지 않으므로
커밋이 실행되기 전에 자동으로 플러시를 호출한 후 커밋이 진행된다.
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
JQPL 같은 객체지향 쿼리는 SQL로 변환되어 영속성 컨텍스트가 아닌 데이터베이스에서
작업을 수행하는데 이 때 변경된 내용을 플러시하지 않으면 데이터베이스는
변경된 내용이 반영되지 않은 상태기 때문에 원활한 SQL 작업을 할 수 없기 때문에
객체지향 쿼리를 사용할 때도 자동으로 플러시를 호출
em.flush();
플러시를 직접 호출해서 사용하는 것은 영속성 컨텍스트를 강제로 플러시하는 것이기 때문에
테스트 같은 경우를 제외하고 거의 사용하지 않음
FlushModeType.AUTO
FlushModeType.COMMIT
기본값은 AUTO로 커밋과 쿼리를 실행할 때 자동으로 플러시를 호출하는 상태이고
COMMIT으로 설정 시 커밋할 때만 자동으로 플러시를 호출한다.
플러시 후에 영속성 컨텍스트가 비워지는 것은 아니고
영속성 컨텐스트와 데이터베이스 간의 동기화가 이루어지는 것 뿐이다.
준영속
영속성 컨텍스트의 관리에서 벗어난 엔티티로
영속성 컨텍스트의 기능을 사용할 수 없는 상태의 엔티티다.
em.detach(entity);
엔티티 매니저의 detach 메서드를 사용해 수동으로 엔티티를 준영속 상태로 만들 수 있고
해당 엔티티와 관련된 1차 캐시와 쓰기 지연 저장소의 SQL 같은 모든 정보가 삭제됨
당연히 영속성 컨텍스트에서 분리된 상태니 데이터베이스에도 반영되지 않는다.
em.clear();
엔티티 매니저의 clear 메서드는 영속성 컨텍스트 자체를 초기화하는 메서드로
detach 메서드를 영속성 컨텍스트의 모든 엔티티에 적용한다고 볼 수 있다.
em.close();
영속성 컨텍스트를 종료하는 메서드로 영속성 컨텍스트가 사라지니
당연히 영속성 컨텍스트에 영속되어 있던 엔티티들이 준영속 상태로 만들어진다.
detach와 clear를 통해 개발자가 직접적으로 준영속 상태로 만드는 경우는 드물고
대부분 영속성 컨텍스트의 종료를 통해 자동으로 준영속 상태로 만든다.
영속성 컨텍스트에 속하지 않고 기능들도 사용할 수 없으니 비영속 상태와 가깝다고 볼 수 있지만
식별자 값이 없을 수도 있는 비영속 상태와는 다르게 영속된 상태였으므로
반드시 식별자 값을 갖고 있다는 차이점이 있다.
em.detach(entity);
Member newMember = em.merge(entity);
// 준영속 영속 상태인 엔티티를 햇갈리지 않기 위해
// 아래와 같은 코드로 짜는 것이 좋음
entity = em.merge(entity);
준영속 상태의 엔티티를 다시 영속 상태로 만들기 위해서는 병합(merge)을 사용하면 되는데
merge 메서드는 준영속 상태의 엔티티로 새로운 영속 상태의 엔티티를 반환해준다.
즉, newMember라는 엔티티의 값이 수정되고 데이터베이스에 반영되는 것이지
준영속 상태인 entity 엔티티는 수정은 가능하지만 데이터베이스에 반영되지 않는다.
// 비영속 엔티티 생성
Entity entity = new Entity();
// 비영속 병합
entity = em.merge(entity);
병합은 준영속 뿐만 아니라 비영속 상태의 엔티티도 영속 상태로 만들 수 있다.
merge 메서드의 파라미터로 전달 받은 엔티티의 식별자 값으로 영속성 컨텍스트를 우선적으로 조회 후
없는 경우 데이터베이스에서 조회하고 그럼에도 없으면 새로운 엔티티를 생성해서 반환한다.
병합하려는 엔티티가 있든 없든 상관 없이 무조건 적으로 엔티티를 반환하기 때문에
준영속, 비영속 상태와 상관 없이 모두 영속 상태로 만들 수 있는 것
'Back-End > JPA' 카테고리의 다른 글
엔티티 매핑 : 학습을 위한 스키마 자동 생성하기 (0) | 2023.06.19 |
---|---|
엔티티 매핑 : 매핑 어노테이션 (0) | 2023.06.19 |
JPA 시작하기 (0) | 2023.06.01 |
엔티티 매핑 기본 (0) | 2023.05.31 |
JPA (0) | 2023.05.31 |