JPA

[JPA] 변경 감지와 병합

F12:) 2024. 1. 15. 12:43

 

   준영속 엔티티

변경 감지와 병합을 이해하기 위해서는 준영속 엔티티를 이해해야 합니다. 준영속 엔티티영속 상태였다가 영속 상태를 벗어난 엔티티를 의미합니다. 그래서 JPA가 더이상 관리하지 않는 것을 의미합니다.

 

JPA가 관리하는 엔티티인 영속 엔티티는 JPA가 엔티티의 변경분을 자동 감지하여 하나의 트랜잭션이 종료될 때 자동으로 변경분을 DB에 반영해줍니다. 하지만 준영속 상태는 그러한 관리를 받을 수 없기 때문에 이러한 준영속 상태를 영속 상태로 바꾸는 등의 처리가 필요합니다.

 

 

이런 상황을 가정해봅시다.

 

게시판에 존재하는 글을 수정하는 상황입니다. 해당 글은 DB에 이미 등록되어 있으며, 수정하고자 하는 게시글의 정보들이 함께 서버로 넘어옵니다. 이 때, 서버에서는 어떠한 로직으로 처리해야할까요?? 이를 위해서는 아래의 두 가지 방법이 쓰입니다.

 

   변경 감지(Dirty Checking)

사실 가장 추천되는 방식입니다. 변경 감지 준영속 상태 엔티티를 영속 상태로 만들어주어 JPA에게 변경을 감지하여 자동으로 반영해주는 것을 의미합니다.

 

게시글의 정보를 DB에서 찾아옵니다. 찾아온 엔티티는 영속 상태의 엔티티이므로, 변경하고자 하는 값들을 해당 영속 엔티티에 수정해주면 됩니다. 간략한 코드는 아래와 같습니다.

 

@Transactional
void update(Item itemParam){ // itemParam : 게시글의 변경 내용
	Item item = itemRepository.find(itemParam.getId());
    item.setContent(itemParam.getContent);
}

 

itemParam에 게시글의 변경하고자 하는 값들을 받아옵니다. itemParam은 준영속 엔티티입니다.

 

더보기

itemParam은 new로 생성하여 변경하고자하는 값들을 넣어준 것일 뿐입니다. 아마 아래와 같은 코드로 생성되었을 것입니다.

 

Item itemParam = new Item();
itemParam.setId(id);
itemParam.setContent(updateContent);

 

이것은 단순 엔티티이지 절대 영속 엔티티가 아닙니다. 그런데 조금 의아합니다. 준영속 엔티티라 함은, 영속 상태였다가 벗어난 상태를 의미했습니다. 그러면 얘는 영속 상태인 적이 없으니 준영속이 아니지 않나? 라고 생각할 수 있습니다.

 

사실 itemParam이 준영속인 이유는 itemParam이 가지고 있는 값인 Id에 의해서 결정됩니다. itemParam이 가지고 있는 Id는 이미 DB에 있는 정보입니다. 따라서 이러한 경우에도 준영속 엔티티라고 합니다.

 

 

조금 더 준영속 엔티티를 폭넓게 정의하자면, DB에 반영되어있는 엔티티이지만 영속 상태가 아닌 엔티티라고 생각해도 괜찮을 것 같아 보입니다.

 

준영속인 itemParam을 사용하지 않고, itemParam의 값으로 DB에서 엔티티를 찾아옵니다. 이러면 찾아온 엔티티는 영속 엔티티겠죠? 그러면 해당 영속 엔티티의 값을 준영속 엔티티의 값으로 대체하면 됩니다.

 

변경분에 대해서 트랜잭션 커밋 시점에서 자동으로 JPA가 업데이트 SQL을 날려주기 때문에, 해당 과정만 진행해도 변경사항이 자동으로 반영됩니다.

 

 

   병합(Merge)

병합은 EntityManager에 있는 merge 메서드를 이용합니다. merge는 권장되는 방식이 아니라 자세하게 설명하지 않고 제가 참고한 몇몇 자료들을 첨부하겠습니다.

 

하지만 merge가 권장되지 않는 이유는 엔티티의 전체 값을 바꿔치기하기 때문입니다.

 

이 것의 단점은 명확합니다. 만약 하나의 값이 누락된 채로 merge를 하게되면? 멀쩡한 엔티티의 값에 null이 생기게 되는 것이죠. 따라서 권장되지 않는 것입니다.

@Transactional
void update(Item itemParam){ // itemParam : 게시글의 변경 내용
	Item mergeItem = em.merge(itemParam);
}

 

이렇게 된다면, 게시글의 수정 내역이 반영됩니다. 이 때, 주의점은 itemParam에 의도적인 null 외에는 모두 채워져있어야한다는 것입니다.

 

그럼 EntityManager는 itemParam에 들어있는 값이 DB의 어느 값인지 알고 찾아서 변경할까요?? 

merge의 로직은 아래와 같습니다.

 

여기서는 Member로 설명되어 있지만, 흐름만을 이해하면 되겠습니다.

 

또한 merge에서 준영속 상태의 값을 merge한 후에는 merge의 return 엔티티를 사용해야 영속 엔티티를 사용하게 되므로 꼭 유의해야합니다.

 

'JPA' 카테고리의 다른 글

[JPA] @Id의 마법(feat. @GenerateValue)  (0) 2023.08.18
[JPA] 복합키 등록하기  (0) 2023.08.18
@Transactional의 선언 위치  (0) 2023.08.17
DTO, DAO란? (DAO와 Repository의 차이)  (0) 2023.08.10
임베디드 타입(Embedded Type)이란?  (0) 2023.08.08