티스토리 뷰
예시 상황 > form을 통해 받아온 상품 정보로 DB값을 업데이트 하려고 한다
먼저 병합(merge) 기능을 사용한 케이스를 확인해보자.
Book 엔티티를 새로 생성해서 form에 받아온 값을 세팅한 후 해당 객체를 서비스 계층의 saveItem 함수의 인자로 넘긴다.
ItemController에서 Book 엔티티에 id 값을 세팅한 채로 인자로 넘겼기 때문에, merge 함수를 타게된다.
이 때, 이 Book 엔티티는 트랜잭션 바깥에서 임의로 생성하여 영속성 컨텍스트가 관리하지는 않지만, 유효한 식별자 값을 가지고 있다. 이러한 엔티티를 준영속 엔티티라고 한다.
→ 준영속 엔티티를 영속 상태로 변경할 때 사용하는 기능이 병합(merge)이다.
여기서는, em.merge(item)의 반환 값이 영속 상태가 된 엔티티이고, item은 그대로 준영속 상태이다.
※ 병합(merge) 동작 방식
|
이 로직을 변경 감지(Dirty Checking) 기능으로 대체해보자.
ItemController에서는 수정 대상의 (form을 통해 받아온) 식별자 값과 수정할 값들을 서비스 계층 함수로 넘긴다.
서비스 계층의 트랜잭션 안에서 전달받은 식별자를 통해 영속된 엔티티를 조회해온 후, 해당 엔티티의 값을 수정해준다. 트랜잭션 커밋 시점에 변경을 감지하여 DB에 해당 속성의 update를 실행한다. 따라서 병합 케이스와 달리 ItemRepository에 별도의 수정 메서드를 호출할 필요가 없다.
가장 큰 차이점은, 병합(merge)의 경우 모든 필드를 교체하고, 변경 감지(Dirty Checking)는 원하는 필드만 선택하여 변경이 가능하다는 것이다. 만약 form으로 변경할 엔티티의 필드 중 하나가 null값으로 넘어온다면, 그것 또한 덮어쓰게 되는 것이다. 실무에서는 보통 변경 가능한 데이터만 form으로 노출하기 때문에..!! 병합(merge) 보다는 변경 감지(Dirty Checking) 기능을 사용하여 변경점을 제한하는 것이 바람직하겠다
- 주어진 예시처럼 컨트롤러에서 엔티티를 생성하는 것은 지양하자
- 컨트롤러는 되도록 트랜잭션이 있는 서비스 계층에서 처리할 수 있도록, 식별자와 변경 데이터만 명확하게 전달하는 것이 좋다
- 서비스 계층의 트랜젝션 안에서 영속 상태의 엔티티를 조회하고 변경하도록 하자
- @org.hibernate.annotations.DynamicUpdate 로 동적으로 SQL 생성되도록 설정
'Spring' 카테고리의 다른 글
컴포넌트 스캔과 수동 Bean 등록 (0) | 2022.04.24 |
---|---|
OSIV (0) | 2022.04.24 |
JPA N+1문제 (0) | 2022.04.23 |
도메인 모델 패턴 / 트랜잭션 스크립트 패턴 (0) | 2022.04.21 |
의존 관계 주입(Dependency Injection) 방법 (0) | 2022.04.20 |