영속성 관리

원종서·2022년 2월 11일
1

JPA

목록 보기
4/13

JPA가 제공하는 기능은 엔티티와 테이블을 매핑하는 설계부분,
매핑한 엔티티를 실제 사용하는 부분으로 나눌 수 있다

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

엔티티매니저는 엔티티를 저장, 수정, 삭제 조회 등 엔티티와 관련된 모든 일을 한다.
엔티티를 저장하는 가상의 데베라고 생각 할 수 잇다.

엔티티매니저는 엔티티매니저팩토리로 만들수 있는 팩토리는 비용이 많이 들기에 한 데이터베이스 당 하나의 팰토리를 만들고 이를 공유해서 사용한다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory(...); 
// 파라미터로는 XML 에 설정한 persistent-unit 의 이름이다.

엔티티 매니저가 필요할 때마다 팩토리에서 만들면 된다.

EntityManager em = emf.createEntityManager();

엔티티 매니저 팩토리는 여러 스레드가 동시 접근해도 안전한다. 반면에 엔티티 매니저는 여러 스레드가 동시 접근하면 동시성 문제가 발생한다.

엔티티매니저는 데베 연결이 꼭 필요한 시점에 데베 커넥션을 획득한다. (트랜잭셔을 시작할 때)

영속성 컨텍스트

엔티티를 영구 저장하는 환경 이라고 생각하면 된다.

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

em.persist(member); // 맴버 엔티티를 매니저를 통해 영속성 컨텍스트에 저장한다.

영속성 컨텍스트는 엔티티 매니저를 생성할 때 하나 만들어진다. ( 여러 엔티티 매니저가 하나의 영속성 컨텍스트에 접근 할 수도 있다)

엔티티 생명주기

  1. 비영속
    엔티티를 생성한 직후, 영속성 컨텍스트에 저장하지 않은 상태

  2. 영속
    엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장한 후의 상태
    영속성 컨텍스트가 관리하고 있는 엔티티의 상태를 영속 상태라고 한다.

em.persist() or em.find() 의 파라미터로 넘어간 엔티티는 영속 상태가 된다.

  1. 준영속
    영속성 컨텍스트가 관리하던 영속 상태의 엔티티가 영속성 컨텍스트로부터 관리 대상이 아니게 된 상태
    em.detach(), em.close() , em.clear() 사용 시 준영속상태가 된다.

  2. 삭제
    em.remove()를 이용해 데베와 영속성 컨텍스트에서 엔티티를 삭제한다.

영속성 컨텍스트의 특징

영속성 컨텍스트와 식별자 값

영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다, 따라서 영속상태는 식별자 값이 반드시 있어야한다.

영속성 컨텍스트와 데베 저장

JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데베에 저장한다 이를 flash라고 한다

영속성 컨텍스트가 엔티티가 관리하면 생기는 장점

1. 엔티티조회

영속성 컨텍스트는 내부에 캐시를 갖고 있다 (1차 캐시). 영속 상태 엔티티는 모두 1차 캐시에 저장된다.
키가 식별자, 값이 엔티티인 Map이라고 생각할 수 있다.

em.find(id) 를 하면 먼저 1차 캐시에서 id에 맞는 식별자 키를 갖고 있는 엔티티를 찾고 없으면 데베에서 찾는다.
데베에서 엔티티를 찾은 경우에도 1차 캐시에 찾은 엔티티를 저장한 후 반환한다. (즉 찾은 데이터는 영속상태가 된 후 반환된다)

영속성 컨텍스트에 있는 엔티티를 여러번 호출해도 같은 (동일성(==) 을 보장하는) 객체를 반환한다.

엔티티 등록

EntityManager em = emf.createEntityManager();
EntityTransaction tr = em.getTransaction();

tr.begin(); // 1.

em.persist(memberA);
em.persist(memberB); // 2.

tr.commit(); // 3.
  1. 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야한다.
  2. persist()를 호출해도 아직 데베의 값은 바뀌지 않는다 ( 1차캐시 내의 엔티티 값만 변경된다)
  3. 데베의 데이터에 쿼리를 날린다.

2번 단계에서 중요한 것이 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데베에 엔티티를 저장하지 않고, 내부 쿼리 저장소에 쿼리문들을 모아둔다. 그리고 트랜잭션 커밋 시 저장소의 쿼리를 데베에 보내는데 이를 트랜잭션을 지원하는 쓰기 지원 이라고 한다.

3번에서 커밋을 하면 엔티티 매니저는 영속성 컨텍스트를 플러시한다 (영속성 컨텍스트의 변경 내용을 데베에 동기화하는 작업이다.). 이때 데베에 쿼리를 날린다. 이렇게 영속성 컨텍스트의 변경 내용을 데베에 동기화 한 실제 데베에 트랜잭션을 커밋한다.

엔티티 수정.

변경 감지라는 기술로 JPA는 엔티티를 변경하고 데베의 행 값을 바꾼다.

Member memberA = em.find(Member.class, "memberA");

memberA.setUsername("newName");

tr.commit();

JPA로 엔티티를 수정할 때 단순히 엔티티를 조회해서 ( 영속상태) 로 만들어서 데이터만 변경하면 된다.

그렇다면 변경 감지 란 무엇일까 .
JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해둔다 (이를 스냅샷이라고 한다). 그리고 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
변경된 엔티티가 있으면 수정쿼리를 작성해서 쓰기지연 sql 저장소에 보관한다.
이후 저장소의 쿼리를 데베에 보내고 데베를 트랜잭션 커밋한다.

  • 변경감지로 인해 변경된 엔티티의 필드 값이 하나이더라도 sql은 모든 테이블 컬럼값들을 업데이트한다.

엔티티 삭제

엔티티 삭제 역시 영속 상태인 엔티티여야한다.

Member memberA = em.find(Member.class, "memberA");
em.remove(memberA);

em.remove 하는 순간 영속성 컨텍스트에서의 엔티티의 값은 삭제 된다.

플러시

플러시는 영속성 컨텍스트의 변경 내용을 데베에 반영한다

단계
1. 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다. 만약 존재하면 수정 쿼리를 만들어 쓰기지연 쿼리 저장소에 등록한다.
2. 쓰기 지연 sql 저장소의 쿼리를 데베에 전송한다.

플러시 하는 방법 3
1. em.flush()
2. 트랜잭션 커밋 시 자동 플러시 호출
3. JPQL 쿼리 실행 시 플러시 자동 호출.

준영속

영속성 컨텍스트가 관리하던 엔티티가 분리된 것을 준영속 상태라고 한다.

준영속 상태 전환
1. em.detach(entity);
2. em.clear();
3. em.close();

  • detach() 는 파라미터의 엔티티만 준영속으로 만든다. 해당 메서드를 사용하면 1차 캐시부터 쓰기 지연 sql 저장소까지 해당 엔티티를 관리하던 모든 정보가 제거된다.

  • clear() 는 영속성 컨텍스트를 초기화해서 영속성 컨텍스트의 모든 엔티티를 준영속 상태로 만든다.

  • close() 영속성 컨텍스트 종료하면 영속성 컨텍스트관리하던 영속 상태 엔티티들은 준영속 상태가 된다.

준영속 특징.
1. 거의 비영속 상태에 가깝다
2. 식별자 값을 갖고 있다
3. 지연로딩을 할 수 없다.

병합 merge()

준영속 상태의 엔티티를 다시 영속 상태로 만들때 사용하는 메서드다.
merge()는 준영속 상태의 엔티티를 받아서 그 정보로 새로운 영속 상태의 엔티티를 반환한다

0개의 댓글