안녕하세요. 오늘은 jpa가 엔티티를 어떻게 관리하는 지 에 대해서 알아보려고 합니다.
김영한님의 자바 orm 표준 jpa 프로그래밍을 읽고 정리했습니다.
엔티티 매니저 팩토리와 엔티티 매니저
엔티티 매니저 팩토리는 엔티티 매니저를 만드는 공장이다. 이를 만드는 비용이 커서 한 개만 만들어서 공유하도록 설계되어 있다.
엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하지만 엔티티 매니저는 여러 스레드에서 공유하여 사용하면 동시성 문제가 생길 수 있어서 공유하면 안된다.
요청2의 엔티티 매니저는 db의 커넥션 풀을 사용하고 있는데 요청1의 엔티티 매니저는 사용하고 있지 않다.
엔티티 매니저는 db 연결이 꼭 필요한 시점까지 커넥션 풀을 얻지 않는다. 보통 트랜잭션을 시작할 때 커넥션을 획득한다.
영속성 컨텍스트란?
영속성 컨텍스트는 엔티티를 영구 저장하는 환경이다.
아래의 코드를 실행하게 되면 엔티티 매니저를 사용하여 회원 엔티티를 영속성 컨텍스트에 저장하게 된다.
영속성 컨텍스트는 논리적인 개념으로 눈에 보이지는 않지만 엔티티 매니저를 통해 접근하고 관리할 수 있다.
em.persist(member);
엔티티 생명주기
엔티티는 4가지의 상태가 존재한다.
비영속 (new / transient)
영속성 컨텍스트와 전혀 관계가 없는 상태
엔티티 객체를 생성했지만 순수한 객체 상태이며 아직 영속성 컨텍스트에 저장되지 않는 상태를 뜻한다.
영속 (managed)
영속성 컨텍스트에 저장된 상태
영속성 컨텍스트에 의해 관리되는 엔티티 객체이다.
준영속 (detached)
영속성 컨텍스트에 저장되었다가 분리된 상태
em.detach(member) 또는 em.close()로 영속성 컨텍스트를 닫거나 em.clear()로 영속성 컨텍스트를 초기화하면 영속 상태의 엔티티가 준영속 상태가 된다.
삭제 (removed)
삭제된 상태
엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.
아래는 이 4가지 상태를 설명하는 그림이다.
영속성 컨텍스트의 특징
1. 영속성 컨텍스트와 식별자의 값
영속 상태는 식별자의 값이 반드시 있어야한다. 이 식별자의 값은 @Id로 테이블의 기본 키와 매핑한 값이다.
2. 영속성 컨텍스트와 데이터베이스 저장
영속성 컨텍스트에 저장되어 있던 엔티티가 데이터베이스에 저장되는 순간은 트랜잭션을 커밋할 때이다. 이를 flush 라고 한다.
엔티티 조회
영속성 컨텍스트 내부에는 캐시를 갖고 있는데 이것을 1차 캐시라고 한다. 영속 상태의 엔티티는 1차캐시에 저장된다.
@Id가 member1인 엔티티를 조회할 때, 1차 캐시에서 member1인 엔티티를 찾고 있다면 반환한다.
1차 캐시에 없다면 데이터베이스에서 조회하고 엔티티를 1차 캐시에 저장한 뒤, 반환한다.
1차 캐시에 있는 동일한 엔티티 인스턴스를 반환한 것이므로 a와 b는 동일하다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
a == b ??
엔티티 등록
엔티티 매니저는 트랜잭션을 커밋하기 전까지 내부 쿼리들을 쓰기 지연 sql 저장소에 보관한다.
그리고 트랜잭션을 커밋할 때, 모아둔 쿼리를 데이터베이스에 보내는데 이것을 트랜잭션을 지원하는 쓰기 지연이라고 한다.
memberA와 memberB를 영속화하면 1차 캐시에 엔티티가 저장되고, 쓰기 지연 sql 저장소에 insert 쿼리가 생성된다.
트랜잭션을 커밋하면 엔티티 매니저는 영속성 컨텍스트를 플러시한다. 플러시는 영속성 컨텍스트의 내용과 데이터베이스 내용을 동기화하는 작업이다. 이 말인즉슨 쓰기 지연 sql 저장소에 있는 쿼리를 데이터베이스에 날리는 작업이다. 이후에 트랜잭션을 커밋한다.
엔티티 수정
jpa는 em.update() 메서드를 지원하지 않는다.
jpa는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 스냅샷의 형태로 복사하여 1차 캐시에 저장한다. 그리고 플러시 시점에 엔티티와 스냅샷을 비교해서 update 쿼리를 만든 후 데이터베이스에 반영하고 커밋한다.
이렇게 엔티티의 변경사항을 데이터베이스에 자동으로 반영하는 기능을 변경 감지 기능, dirty checking 이라고 하고 이는, 영속 상태의 엔티티에서만 적용이 된다.
jpa의 update 쿼리 기본 전략은 모든 필드를 업데이트 하는 것이다.
이렇게 모든 필드를 사용하는 것은 데이터베이스에 보내는 데이터 전송량이 증가하는 단점이 있지만, 수정 쿼리가 항상 같고, 동일한 쿼리를 보내면 데이터베이스에서 이전에 파싱된 쿼리를 재사용할 수 있다는 장점이 있다.
UPDATE MEMBER
SET
NAME=?,
AGE=?,
GRADE=?,
....
WHERE
ID=?
만약 수정된 데이터만 사용하여 update 쿼리를 생성하고 싶다면 하이버네이트 확장 기능을 사용한다.
@org.hibernate.annotations.DynamicUpdate
엔티티 삭제
아래의 코드를 사용하면 대상 엔티티를 삭제할 수 있다.
이 또한, 바로 삭제하는 것이 아니라 삭제 쿼리를 쓰기 지연 sql 저장소에 등록하게 된다. 그 후, 트랜잭션을 커밋하여 플러시를 호출하게 되면 엔티티가 데이터베이스에서 삭제된다.
em.remove(member);
플러시
플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 작업이다.
영속성 컨텍스트를 플러시하는 방법은 3가지가 있다.
1. em.flush() 를 호출한다.
flush 메서드를 호출하여 영속성 컨텍스트를 강제로 플러시하는 것으로 거의 사용하지 않는 방법이다.
2. 트랜잭션 커밋 시 플러시가 자동 호출된다.
플러시 하지 않고 트랜잭션만 커밋하면 어떤 데이터도 데이터베이스에 반영되지 않는다. 따라서 커밋하기 전에 플러시를 꼭 호출하여 변경 내용을 데이터베이스에 반영해야한다.
jpa는 이 문제를 예방하기 위해 트랜잭션 커밋 시 플러시를 자동 호출하고 있다.
3. JPQL 쿼리 실행 시 플러시가 자동 호출된다.
아래의 코드를 보면 엔티티들은 영속성 컨텍스트에 있지만 아직 데이터베이스에 반영되지 않았다. 이렇게 되면 jpql 을 실행하면 어떤 엔티티도 쿼리 결과로 조회되지 않는다. 그래서 jpa는 이러한 일을 방지하기 위해서 jpql을 실행할 때, 플러시를 자동 호출한다.
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
플러시는 영속성 컨텍스트에 보관된 엔티티를 지우는 것이 아니라 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 것이다.
준영속
준영속 상태는 영속성 컨텍스트에 분리된 상태로 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
datach 메서드는 아래와같이 정의되어 있다.
public void detach(Object object);
detach 메서드가 호출되면 해당 엔티티를 1차 캐시에서 삭제하고, 관련 쿼리를 쓰기 지연 sql 저장소에서 삭제한다.
detach는 하나의 엔티티에 대해 준영속 상태로 만들어 주었다면 아래의 메서드는 영속성 컨텍스트를 초기화하거나 종료해버림으로써 모든 엔티티를 준영속 상태로 변화시킨다.
em.clear() // 영속성 컨텍스트 초기화
em.close() // 영속성 컨텍스트 닫기 (종료)
준영속 상태는 거의 비영속 상태와 가까우며 지연 로딩을 할 수 없다. 하지만 비영속에 경우 식별자의 값이 없을 수 있지만 준영속 상태는 이미 영속 상태였기 때문에 식별자 값을 가지고 있다.
준영속 상태의 엔티티를 다시 영속 상태로 변경하는 방법은 merge 메서드를 사용하는 것이다.
new 였던 (비영속) 엔티티도 merge 메서드를 통해 영속 상태로 변화시킬 수 있다.
public <T> T merge(T entity);
Member mergeMember = em.merge(member);
아래는 스터디하면서 만든 피피티 자료입니다.
'🍀spring > 스프링 jpa' 카테고리의 다른 글
[Spring JPA] 프록시와 연관관계 관리 (0) | 2024.03.03 |
---|---|
[Spring JPA] 고급 매핑 (상속, 복합 키 매핑) (0) | 2024.02.29 |
[Spring JPA] 다양한 연관관계 매핑 (1) | 2024.02.29 |
[Spring JPA] 연관관계 매핑 기초 (1) | 2024.02.28 |
[Spring JPA] 엔티티 매핑 (1) | 2024.02.26 |