📌 엔티티 매니저 팩토리와 엔티티 매니저
🧷 엔티티 매니저 팩토리
- 팩토리를 만들기 위해서는 많은 비용을 요구한다. 하나만 만들어서 애플리케이션 전체에서 사용한다.
- 설정 정보(persistence.xml)를 바탕으로 생성한다.
- 서로 다른 스레드 간에 공유해도 문제가 없다.
🧷 엔티티 매니저
- 생성하는데 비용이 거의 들지 않는다.
- 동시성 문제 때문에 스레드 간에 공유 금지.
- 필요한 시점까지 커넥션을 얻지 않는다. 보통 트랜잭션을 시작할 때 커넥션을 얻는다.
엔티티 매니저 팩토리를 생성할 때 커넥션풀도 만드는 방법은 J2SE 환경에서 사용하는 방법이다.
대부분의 JPA 구현체들이 사용하는 방법이다.
📌 영속성 컨텍스트란?
JPA를 이해하는데 가장 중요한 용어는 영속성 컨텍스트(persistence context)이다.
엔티티 매니저가 엔티티를 보관하고 관리하는 곳이다.
엔티티 매니저가 만들어질 때 하나의 영속성 컨텍스트도 만들어진다. 일단은
📌 엔티티의 생명주기 (4가지 상태)
1️⃣ 비영속, new / transient
엔티티를 생성하고 저장하지 않은 상태. 달리 표현하면 persist() 호출 전
// Example, 비영속 상태
Member member = new Member();
merber.setId("member1");
member.setUsername("희조");
2️⃣ 영속, managed
엔티티 매니저로 엔티티를 영속성 컨텍스트에 저장한 상태. 달리 표현하면 persist(), merge() 호출 후
영속 상태는 즉, 영속성 컨텍스트에 의해 관리된다는 뜻이다.
3️⃣ 준영속, detached
영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않는 상태.
달리 표현하면 detach() 을 호출하거나 close() 영속성 컨텍스트를 닫거나 clear()로 초기화한 후
4️⃣ 삭제, removed
엔티티를 영속성 컨텍스트와 데이터베이스에 삭제한다. 달리 표현하면 remove() 실행 후
📌 영속성 컨텍스트의 특징
- 영속성 컨텍스트는 엔티티를 식별자로 구분하기 때문에 영속 상태의 엔티티는 @Id가 필수, 없으면 예외
- JPA는 트랜잭션을 커밋할 때 영속성 컨텍스트에 저장된 엔티티를 데이터베이스에 반영한다.
- 그 외에 1차 캐시, 동일성 보장, 쓰기 지연, 변경 감지, 지연 로딩을 지원한다.
1️⃣ 엔티티 조회
영속성 컨텍스트 내부에 모든 영속 상태의 엔티티를 저장하는 캐시가 있다. (1차 캐시)
이 캐시는 (Key : 식별자, Value : 인스턴스)를 가지는 Map이다.
find() 를 호출하면 1차 캐시에서 먼저 조회하고 없으면 데이터베이스에서 조회한다.
데이터베이스에서 조회 시 엔티티를 생성해서 1차 캐시에 저장한 다음 영속 상태의 엔티티를 반환한다.
// 영속 엔티티의 동일성을 보장한다!
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // True
2️⃣ 엔티티 등록
엔티티 매니저는 INSERT 쿼리들은 영속성 컨텍스트 내부 쿼리 저장소에 모아둔다.
이후 트랜잭션을 커밋할 때 모아둔 쿼리들을 데이터베이스에 보낸다. (트랜잭션을 지원하는 쓰기 지연)
커밋하기 이전에 플러시(변경 사항을 데이터베이스에 동기화하는 작업)를 수행한다.
등록 쿼리를 아무리 보내도 커밋되지 않으면 반영되지 않아 트랜잭션을 지원하는 쓰기 지연이 가능하다.
3️⃣ 엔티티 수정
SQL을 사용해 수정 쿼리를 직접 작성하면 보통 비지니스 로직이 SQL에 의존하게 된다.
하지만 JPA에서 엔티티 수정은 단순히 엔티티를 조회한 뒤 데이터만 수정해주면 된다.
여기서 변경사항을 자동으로 감지하는 기능을 변경 감지, dirty checking이라고 한다.
영속성 컨텍스트에 보관할 때 최초 상태를 가지는 스냅샷을 같이 저장한다. (영속 상태)
플러시 시점에 스냅샷과 엔티티를 비교해서 변경사항을 파악한다.
수정된 필드만 반영할 것 같지만 JPA의 기본 전략은 모든 필드를 수정에 반영한다.
모든 필드를 수정하는 이유는 다음과 같다.
- 모든 필드를 사용하면 수정 쿼리가 항상 같다. 애플리케이션 로딩 시점에 미리 생성하고 재사용할 수 있다.
- 데이터베이스에 동일한 쿼리를 보내면 이미 파싱된 쿼리라서 재사용할 수 있다.
너무 필드가 많거나 저장되는 내용이 크면 동적으로 UPDATE SQL을 생성하는 전략을 사용하면 된다.
4️⃣ 엔티티 삭제
삭제하기 위해선 대상을 먼저 조회를 해야 한다.
// Example, Remove Entity
Memeber memberA = em.find(Member.class, "memberA");
em.remove(memberA);
엔티티 등록과 비슷하게 삭제 쿼리를 내부 쿼리 저장소에 저장한 후 커밋 시 반영된다.
remove() 를 호출하면 바로 영속성 컨텍스트에 삭제된다.
📌 플러시
flush() 를 호출하면 다음과 같은 작업을 수행한다.
- 변경 감지가 동작한다. 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 저장한다.
- 쓰기 지연 SQL 저장소에 저장된 쿼리를 데이터베이스에 전송한다.
다음 3가지 방법으로 플러시 할 수 있다.
- 직접 호출 : 메서드를 직접 호출해 강제로 플러시 한다. 잘 안 쓰인다.
- 트랜잭션 커밋 : JPA는 커밋할 때 자동으로 플러시를 호출한다.
- JPQL 쿼리 실행 : 객체지향 쿼리를 호출할 때 자동으로 플러시를 호출한다.
✔️ 플러시 모드 옵션
엔티티 매니저에 플러시 모드를 직접 지정하려면 javax.persistence.FlushModeType을 사용하면 된다.
- FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본값)
- FlushModeType.COMMIT : 커밋할 때만 플러시
📌 준영속
영속 상태의 엔티티가 영속성 컨텍스트에 분리된 상태를 준영속 상태라고 한다.
영속성 컨텍스트가 관리하지 않는 상태이기 때문에 당연히 제공하는 기능을 사용할 수 없게 된다.
1️⃣ 준영속 상태로 전환 : detach()
detach() 를 호출하면 영속성 컨텍스트 내 엔티티에 관한 모든 정보가 제거된다.
1차 캐시에 있는 엔티티는 물론 쓰기 지연 SQL 저장소에 저장된 쿼리까지 제거된다.
2️⃣ 초기화 : clear()
detach() 가 하나만 준영속 상태로 만든다면 clear() 는 영속성 컨텍스트를 전부 준영속 상태로 만든다.
말 그대로 새로 만든 것과 같은 상태로 만드는 것이다.
3️⃣ 종료 : close()
말 그대로 영속성 컨텍스트의 활동을 종료해 모든 엔티티를 준영속 상태로 만든다.
4️⃣ 병합 : merge()
merge() 메서드를 사용해 비영속·준영속 상태를 영속 상태로 변경할 수 있다.
합친다는 개념이 아닌 정보를 받아와 새로운 객체를 리턴하는 형태이다.
따라서 매개변수로 넘어온 엔티티는 병합 후에도 이전 상태로 남아있다.
✔️ 준영속 상태의 특징
준영속 상태는 거의 비영속 상태에 가깝지만 이미 한번 영속 상태였기 때문에 @Id 값이 존재한다.
더 이상 영속성 컨텍스트가 관리하지 않는 상태이기 때문에 지연 로딩을 할 수 없다.
'개인 공부 > JPA (자바 ORM 표준 JPA 프로그래밍)' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 다양한 연관관계 매핑 (6장) (0) | 2022.12.02 |
---|---|
자바 ORM 표준 JPA 프로그래밍 : 연관관계 매핑 기초 (5장) (0) | 2022.11.18 |
자바 ORM 표준 JPA 프로그래밍 : 엔티티 매핑 (4장) (1) | 2022.10.06 |
자바 ORM 표준 JPA 프로그래밍 : JPA 시작 (2장) (0) | 2022.09.15 |
자바 ORM 표준 JPA 프로그래밍 : JPA 소개 (1장) (0) | 2022.08.12 |
댓글