문제 발생
Entity를 수정하려고 하는 데, Entity는 DB와 밀접한 관련이 있고, @Setter를 사용하는 것은 값이 변화하는 지점을 예측하기 힘들기때문에 @Setter를 사용하는 것을 지양해야한다.
그렇다면, setter를 사용하지 않고 어떻게 Entity를 update할 수 있을까??
문제 분석
먼저, 엔티티가 어떻게 수정되고 있는 지부터 알아보자!!
엔티티를 조회하는 쿼리를 실행시키면, 찾는 엔티티가 영속성 컨텍스트의 1차 캐시에 있다면 그 엔티티를 가져오고 만약 없다면, DB에서 조회한 후 1차 캐시에 저장하고 엔티티를 반환한다.
이때, JPA는 엔티티의 변경사항을 DB에 자동으로 반영하는 데 반영할 때 사용하는 메서드가 flush이다.
flush 메서드를 사용하면, 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾아 만약 변경된 것이 있다면 그에 따른 수정 쿼리를 작성해 쓰기 지연 SQL 저장소로 보낸다.
최종적으로, 쓰기 지연 저장소의 SQL을 DB로 보낸다.
해결 방법
해결 방법은 무척 간단하다.
'update가 필요한 값들만 모아서, 수정한다! 라는 의미를 가진 메서드를 만들면 된다.'
update가 필요한 부분은 userMoney이기 때문에, updateMoney라는 메서드를 만들어준다.
@Entity
public class User {
private Integer userMoney;
...
public void updateMoney(Integer userMoney){
this.userMoney = userMoney;
}
}
다음으로, updateMoney 메서드를 이용해서 userMoney를 update해줍니다.
@Transactional
public Diary saveDiary(DiaryRequestForm diaryRequestForm){
LocalDate localDate = DateJudgementUtil.checkSavingDate(diaryRequestForm.getLocalDateTime());
User user = userRepository.findById(diaryRequestForm.getUserId()).get();// 로그인 할 때 필터링이 되있기 때문에 null 체크 안 함
Diary diary = diaryRequestForm.toEntity(localDate, user);
diaryRepository.save(diary);
// 돈 증가
Integer judgeMoney = MoneyManager.judgeMoney(diaryRequestForm.getContent());
Integer increasedMoney = MoneyManager.earnMoney(user.getUserMoney(), judgeMoney);
user.updateMoney(increasedMoney);
return diary;
}
위 코드를 설명하자면 아래와 같다.
1. findById로 User객체를 가져온다. -> 이때 이 객체는 JPA의 영속성 컨텍스트에의해 관리된다.
2. 영속성 컨텍스트에의해 관리가 된다는 것은 객체의 최초상태를 복사해서 영속성 컨텍스트에 저장해두는 것을 뜻한다.(이를 스냅샷이라 한다.)
3. 이때, 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾으면 수정 쿼리를 생성해서 데이터베이스에 반영해준다.
-> 이를 변경 감지 또는 더티 체킹이라고 한다.
결론
@setter를 사용하지 않고도, 값을 변경할 수 있는 방법은 UPDATE하는 메서드를 만들어주는 것이다.
![](https://t1.daumcdn.net/keditor/emoticon/niniz/large/002.gif)