본문 바로가기

공부 기록/Java

[JPA] 3장 - 영속성 관리

엔티티 매니저

- 엔티티를 저장, 수정, 삭제, 조회하는 등 엔티티와 관련된 모든 일을 처리한다.

- 개발자 입장에서는 엔티티를 저장하는 가상의 데이터베이스로 생각하면 된다.

 

엔티티 매니저 팩토리와 엔티티 매니저

엔티티 매니저 팩토리 생성하기

- 공장 만들기 => 비용이 아주 많이 든다.

- 데이터베이스를 하나만 사용하는 애플리케이션은 일반적으로 하나만 생성한다. => 애플리케이션 전체에서 공유하도록 설계되어 있다.

- 여러 스레드가 동시에 접근해도 안전하다. => 공유해도 문제 없다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");

 

엔티티 매니저 생성하기

- 비용이 거의 안 든다

- 여러 스레드가 동시에 접근하면 동시성 문제가 발생한다. => 스레드 간에 절대 공유하면 안 된다.

EntityManager em = emf.createEntityManager();

영속성 컨텍스트

- 엔티티를 영구 저장하는 환경

- 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

- 엔티티 매니저를 사용해서 회원 엔티티를 영속성 컨텍스트에 저장하는 코드

em.persist(member);

 

엔티티의 생명 주기

- 비영속 : 영속성 컨텍스트와 전혀 관계가 없는 상태

- 영속 : 영속성 컨텍스트에 저장된 상태

- 준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태

em.detach(); // 특정 엔티티를 준영속 상태로 만든다
em.close(); // 영속성 컨텍스트를 닫는다
em.clear(); // 영속성 컨텍스트를 초기화

- 삭제 : 삭제된 상태

em.remove(member); // 엔티티를 영속성 컨텍스트와 DB에서 삭제

 

영속성 컨텍스트의 특징

- 엔티티를 식별자 값(@Id)으로 구분한다. => 영속 상태는 식별자 값이 반드시 있어야 한다.

- 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다. => 플러시

 

영속성 컨텍스트가 엔티티를 관리할 때의 장점

- 1차 캐시 : 엔티티 조회 시 데이터베이스 조회 전 1차 캐시에서 엔티티 조회 => 성능상 이점

- 동일성 보장

   * 동일성(identity) : 실제 인스턴스가 같다. == 이 참이다.

   * 동등성(equality) : 실제 인스턴스는 다를 수 있지만 가지고 있는 값이 같다.

- 트랜잭션을 지원하는 쓰기 지연 : 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 SQL을 차곡차곡 모아두고, 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 보낸다. => 성능을 최적화할 수 있다.

- 변경 감지 : 플러시 시점에 스냅샷과 엔티티를 배교해서 변경된 엔티티를 찾고, 엔티티의 모든 필드를 업데이트한다.

   * 스냅샷 : JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해둔다.

- 지연 로딩


플러시

- 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.

변경 감지 동작 -> 영속성 컨텍스트 안의 모든 엔티티를 스냅샷과 비교하여 수정된 엔티티 찾기 -> 쓰기 지연 SQL 저장소에 수정 쿼리 등록 -> 쿼리를 DB에 전송

 

영속성 컨텍스트를 플러시하는 방법

1. em.flush() 직접 호출

2. 트랜잭션 커밋 시 자동 호출

3. JPQL 쿼리 실행시 자동 호출


준영속

- 영속성 컨텍스트가 관리하는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태

- 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.

 

준영속 상태로 만드는 방법

1. em.detach(entity) : 특정 엔티티만 준영속 상태로 전환

2. em.clear() : 영속성 컨텍스트를 완전히 초기화

3. em.close() : 영속성 컨텍스트를 종료

 

준영속 상태의 특징

- 거의 비영속 상태에 가깝다. => 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다.

- 식별자 값을 가지고 있다. => 이미 한 번 영속 상태였으므로 반드시 식별자 값을 가지고 있다.

- 지연 로딩을 할 수 없다.

   * 지연 로딩(Lazy Loading) : 실제 객체 대신 프록시 객체를 로딩해두고 해당 객체를 실제 사용할 때 영속성 컨텍스트를 통해 데이터를 불러오는 방법

 

병합(Merge())

- 준영속 상태의 엔티티를 다시 영속 상태로 변경한다.

Member mergeMember = em.merge(member);

- 비영속 병합 : 병합은 비영속 엔티티도 영속 상태로 만들 수 있다.

- 파라미터로 넘어온 엔티티의 식별자 값으로 영속성 컨텍스트를 조회, 찾는 엔티티가 없으면 DB에서 조회한다. DB에서도 발견하지 못하면 새로운 엔티티를 생성해서 병합한다. => save or update 기능을 수행한다.