💡 본 게시글은 김영한님의 인프런(Inflearn) 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편에 대해 공부하고, 정리한 내용입니다.
1. 엔티티 매니저 팩토리 & 엔티티 매니저
1) 엔티티 매니저 팩토리
- - 생성 비용이 큼
- - 여러 스레드 동시 접근에 안전
2) 엔티티 매니저
- - 생성 비용이 거의 없음
- - 여러 스레드 동시 접근 시 동시성 문제 발생, 스레드 간 공유 금지
(1) 웹 어플리케이션 구조
- - EntityManagerFactory에서 다수의 엔티티 매니저를 생성
- - 엔티티 매니저는 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않음
- - 보통 트랜잭션을 시작할 때 커넥션을 획득
- - JPA 구현체들은 EntityManagerFactory 생성 시 커넥션 풀도 함께 생성
- - J2EE 환경(스프링 포함)에서 JPA를 사용할 경우, 해당 컨테이너가 제공하는 데이터 소스를 사용
2. 엔티티 생명주기
1) 엔티티 상태
- - 비영속 (new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
- - 영속 (managed): 영속성 컨텍스트에 저장된 상태
- - 준영속 (detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
- - 삭제 (removed): 영속성 컨텍스트와 데이터베이스에서 삭제된 상태
2) 상태별 코드 예시
(1) 비영속
// 객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
(2) 영속
// 객체를 저장한 상태(영속)
em.persist(member);
(3) 준영속
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
// 또는
em.close();
em.clear();
(4) 삭제
// 객체를 삭제한 상태(삭제)
em.remove(member);
3. 영속성 컨텍스트
1) 영속성 컨텍스트 개념
- - 엔티티를 영구 저장하는 환경을 의미
- - 엔티티 매니저로 엔티티를 저장하거나 조회 시 영속성 컨텍스트에 보관 및 관리
- - 여러 엔티티 매니저가 같은 영속성 컨텍스트에 접근 가능
2) 영속성 컨텍스트의 특징
- - 식별자 값 관리: 엔티티는 식별자 값(@Id)으로 구분되며, 영속 상태에서는 식별자 값이 반드시 필요
- - 데이터베이스 저장 시점: 보통 트랜잭션 커밋 시 영속성 컨텍스트에 저장된 엔티티가 데이터베이스에 반영 (플러시)
- - 1차 캐시: 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있으며, 영속 상태의 엔티티가 저장됨
- - 동일성 보장: 1차 캐시에서 동일한 엔티티 인스턴스의 참조를 반환하여 동일성 보장
- - 쓰기 지연: 트랜잭션 커밋 시까지 INSERT SQL을 모아 두었다가 한 번에 실행
- - 변경 감지: 엔티티의 변경사항을 자동으로 감지하여 데이터베이스에 반영
- - 지연 로딩: 필요한 시점까지 데이터베이스에 접근하지 않고, 실제 사용할 때 로딩
4. 엔티티 조회 매커니즘
1) 1차 캐시에서 조회
- em.find() 메소드를 호출하면, 먼저 1차 캐시에서 조회한 후, 존재하지 않으면 데이터베이스에서 조회
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 1차 캐시에 저장됨
em.persist(member);
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
2) 데이터베이스에서 조회
- 1차 캐시에 엔티티가 존재하지 않을 경우, 데이터베이스를 조회하여 엔티티를 생성하고, 1차 캐시에 저장한 후 반환
Member findMember2 = em.find(Member.class, "member2");
3) 영속 엔티티의 동일성 보장
- 1차 캐시에 있는 같은 엔티티 인스턴스의 ‘참조’를 반환하기 때문에 동일성이 보장됩니다
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member2");
System.out.println(a == b); // 동일성 비교, 결과는 참
5. 엔티티 저장 매커니즘
1) 쓰기 지연
- 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않으며, 커밋 시점에 INSERT SQL을 모아서 전달
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
6. 엔티티 변경 매커니즘
1) 변경 감지 (Dirty Checking)
- 엔티티의 변경 사항을 자동으로 감지하여 데이터베이스에 반영하는 기능
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); //[트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
// em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); //[트랜잭션] 커밋
- JPA는 엔티티를 영속성 컨텍스트에 보관할 때 최초 상태를 복사해서 스냅샷을 저장해 둡니다. 플러시 시점에 스냅샷과 엔티티를 비교하여 변경된 엔티티를 찾습니다.
7. 엔티티 삭제 매커니즘
1) 삭제 처리
- 엔티티를 삭제할 때, 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록하고, 트랜잭션을 커밋하여 플러시를 호출하면 실제 데이터베이스에 삭제 쿼리를 전달
Member memberA = em.find(Member.class, "memberA"); // 삭제 대상 엔티티 조회
em.remove(memberA); // 엔티티 삭제
- em.remove(memberA);를 호출하는 순간, memberA는 영속성 컨텍스트에서 제거됩니다.
8. 플러시 (flush)
1) 플러시의 의미
- 플러시(flush)는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 작업을 의미합니다
- - 변경 감지가 동작하여 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교하여 수정된 엔티티를 찾고 수정 쿼리를 쓰기 지연 SQL 저장소에 저장
- - 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
2) 플러시 발생 시점
- - flush() 직접 호출: 엔티티 매니저의
flush()메소드를 직접 호출. - - 트랜잭션 커밋 시 자동 호출.
- - JPQL 쿼리 실행 시 자동 호출: JPQL, Criteria 호출 시에도 플러시가 실행됨.
3) 플러시 모드 옵션
- - FlushModeType.AUTO: 커밋이나 쿼리를 실행할 때 플러시 (기본값).
- - FlushModeType.COMMIT: 커밋할 때만 플러시.
em.setFlushMode(FlushModeType.COMMIT); // EntityManager에 플러시 모드 직접 설정
- 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 것이지, 영속성 컨텍스트의 엔티티를 지우는 작업이 아님을 기억하세요.
9. 준영속 상태
1) 준영속 상태란?
- 준영속 상태란 엔티티가 영속성 컨텍스트에서 분리된 상태를 의미합니다. 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없습니다.
2) 준영속 상태로 변경하는 방법
- - em.detach(entity): 특정 엔티티만 준영속 상태로 전환
- - em.clear(): 영속성 컨텍스트를 완전히 초기화
- - em.close(): 영속성 컨텍스트를 종료
3) 준영속 상태로 전환: detach()
public void testDetached(){
// 회원 엔티티 생성, 비영속 상태
Member member = new Member();
member.setId("memberA");
member.setUsername("회원A");
// 회원 엔티티 영속 상태
em.persist(member);
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
// 트랜잭션 커밋
transaction.commit();
}
- 플러시가 일어나기 전 준영속 상태로 변경했기 때문에 위 코드는 어떤 일도 일어나지 않습니다.
4) 영속성 컨텍스트 초기화: clear()
- 해당 영속성 컨텍스트의 모든 엔티티를 준영속 상태로 만듭니다.
5) 영속성 컨텍스트 종료: close()
- 영속성 컨텍스트가 관리하던 모든 엔티티가 준영속 상태가 됩니다.
6) 병합: merge()
- 준영속 상태의 엔티티를 다시 영속 상태로 변경합니다. 새로운 영속 상태의 엔티티를 반환하며, 파라미터로 넘어온 준영속 엔티티는 병합 후에도 준영속 상태입니다. 비영속 엔티티도 영속 상태로 만들 수 있습니다.
Member member = new Member();
Member newMember = em.merge(member); // 비영속 병합
tx.commit(); // 가능
- 병합은 준영속/비영속을 신경 쓰지 않고, 식별자 값으로 엔티티를 조회할 수 있다면 불러서 병합하고, 그렇지 않다면 새로 생성해서 병합합니다. 따라서 병합은 save or update 기능을 수행합니다.
7) 준영속 상태의 특징
- - 거의 비영속 상태에 가깝습니다.
- - 영속성 컨텍스트가 관리하지 않으므로 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 어떤 기능도 동작하지 않음
- - 식별자 값을 가지고 있습니다. 이미 한 번 영속 상태였으므로 반드시 식별자 값을 가지고 있습니다.
- - 지연 로딩을 할 수 없습니다.
'Spring > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
| 06. 다양한 연관관계 매핑 (1) | 2024.09.08 |
|---|---|
| 05. 연관관계 매핑 기초 (0) | 2024.09.08 |
| 04. 엔티티 매핑 (1) | 2024.09.08 |
| 02. JPA 시작하기 (0) | 2024.08.11 |
| 01. JPA 소개 (0) | 2024.07.24 |