Home JPA - 변경 감지와 병합
Post
Cancel

JPA - 변경 감지와 병합

변경 감지와 병합(merge)

영속성 컨텍스트가 더는 관리하지 않는 엔티티인 준영속 엔티티를 수정하는 방법은 두가지가 있음

  • 변경 감지 기능 사용
  • 병합(merge) 사용

기존에 영속성 컨텍스트에 관리되고 있는 엔티티를 detach()를 통해 더이상 관리하지 않는 준영속 엔티티를 만드는 방법도 있지만, find()를 통해 얻오언 엔티티가 아닌 임의로 생성한 엔티티도 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있음

변경 감지 기능 사용

영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법. 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값으로 수정한 다음, 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)가 동작하여 데이터베이스에 UPDATE SQL 수행.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Transactional
// itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
void update(Item itemParam) {
  // 같은 엔티티를 조회
  Item findItem = em.find(Item.class, itemParam.getId());

  // 데이터를 수정
  findItem.setPrice(itemParam.getPrice());

  // 이후 트랜잭션이 종료 되면서 커밋 수행
  // 커밋 수행시 영속성 컨텍스트가 관리하는 엔티티의 몇몇 필드의 값이 변경되었으므로
  // 이를 감지하여 UPDATE 쿼리 수행
}

병합 사용

merge() 메소드를 사용하여 강제로 엔티티내의 필드의 값을 업데이트 하는 방법

1
2
3
4
5
@Transactional
// itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
void update(Item itemParam) {
    Item mergeItem = em.merge(itemParam);
}

Alt text

  • merge()를 실행
  • 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회
    • 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고 1차 캐시에 저장
  • 조죄한 영속 엔티티에 엔티티의 값을 수정함
  • 영속 상태인 엔티티를 반환


병합시의 동작방식을 정리하면 아래와 같음

  • 준영속 엔티티의 식별자 값으로 영속 엔티티를 조회
  • 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체(병합)
  • 트랜잭션 커밋 시점에 변경 감지 기능이 동작하여 데이터베이트에 UPDATE SQL 수행

주의해야 할 사항은 변경 감지 기능을 사용하면 원하는 필드만 선태하여 값을 변경할 수 있지만, 병합을 사용하면 모든 필드가 변경되기 떄문에 병합시 값이 없으면 null 로 업데이트 할 위험이 있음.

실무에서는 보통 업데이트 기능이 매우 제한적 임. 병합은 모든 필드를 변경해버리고 데이터가 없으면 null로 업데이트 해버리기 떄문에, 보통 변경 가능한 데이터만 노출시키는 실무 환경에서는 병합을 사용하기 까다로움.

그러므로 엔티티를 변경할 떄는 항상 변경 감지를 사용하도록 하는 것이 좋음

  • 컨트롤러에서 어설프게 엔티티를 생성하지 않도록 함
  • 트랜잭션이 있는 서비스 계층에 식별자와 변경할 데이터를 명확하게 전달 하도록 함(파라미터 or DTO)
  • 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고 엔티티의 데이터를 직접 변경하여 트랜잭션 커밋 시점에 변경 감지가 실행 되도록 함

참고

  • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발(김영한)
This post is licensed under CC BY 4.0 by the author.