JPA 영속성 컨텍스트

엔티티를 영구 저장하는 환경
JPA는 영속성 컨텍스트라는 환경에서 엔티티를 관리한다.
엔티티 매니저를 통해 접근

J2SE환경에서는 엔티티 매니저와 영속성 컨텍스트가 1:1 관계이지만
J2EE, 스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1의 관계이다.

엔티티의 상태

영속성 컨텍스트를 기준으로 다음과 같이 엔티티의 상태를 나눌 수 있다.

영속성 컨텍스트의 이점

플러시

영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는것
플러시가 호출되면 변경 감지가 실행되어 수정된 엔티티에 대한 SQL이 쓰기 지연 SQL 저장소에 등록되고 등록된 쿼리들이 DB에 전송된다.
transaction.commit()시 자동으로 호출되며 em.flush()를 통해 직접 호출 할 수 도 있다.

JPQL 쿼리를 실행할때도 플러시가 자동 호출된다. (동시성 보장을 위함)
쿼리 호출시에 플러시를 방지하고 싶다면 em.setFlushMode(FlushModeType.COMMIT)로 설정하면된다.

Merge

JPA에서 수정을 하는법
다음과 같이 Item 엔티티의 update를 처리하는 서비스가 있다고 해보자.
파라미터로 넘어온 Item 엔티티는 영속인지 준영속인지 알 수 없다고 가정하자.

@Transactional
void update(Item item) {
    // ?
}

JPA에서는 어떤 방식으로 엔티티를 수정해야될까?

변경 감지

위에서 알아봤던 변경 감지 기능을 사용하여 수정할 수 있다.
이 경우에는 변경 감지 기능을 사용하기 위해 엔티티를 영속 상태로 만들어 줘야 한다.

@Transactional
void update(Item item) {
    Item findItem = em.find(Item.class, item.getId());
    findItem.changePrice(item.getprice());
}

이 경우 트랜잭션 커밋 시점에 변경 감지가 동작해서 데이터베이스에 update SQL이 실행된다.

병합

병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용한는 기능이다.

@Transactional
void update(Item item) {
    Item mergeItem = em.merge(item);
}
  1. merge() 실행하면
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
    2.1 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고, 1차 캐시에 저장한다.
  3. 조회한 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체한다.(병합한다.)
  4. 영속 엔티티를 반환한다.
  5. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 데이터베이스에 update SQL이 실행된다.

병합의 문제점

변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만
병합을 사용하면 모든 속성이 변경되어 병합시 값이 없으면 null로 업데이트 될 위험이 있다

병합의 문제 해결 방법

엔티티를 변경할 때는 항상 변경 감지를 사용하는것이 좋다.