저장(CREATE)
save를 사용
조회(RETRIEVE)
find나 get을 사용
삭제(DELETE)
delete를 사용
엔티티를 수정하려면(UPDATE)?
Spring Data Jpa에서는 sql의 insert와 update가 모두 save함수로 수행된다. 먼저 Save함수가 어떻게 동작하는지 살펴보자
@Transactional
@Override
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
save함수를 확인해보면 if문으로 분기가 되는데 isNew함수를 실행해
1. 엔티티가 새로운 엔티티면 persist 함수를 실행하고
2. 엔티티가 이미 존재하면 merge함수를 실행한다.
여기서 엔티티를 분기하는 isNew함수는 @Id로 마킹된 엔티티의 ID가 (int, long, char등 인 경우) 0인지 아닌지 확인하거나 (String, Long등 인 경우) null인지 아닌지 확인한다.
이 내용을 살펴본 결과 save함수의 동작 과정은 아래와 같이 정리할 수 있다.
이제 merge가 어떤 식으로 update를 진행시키는지 알아보도록 하자
먼저 영속상태와 준영속 상태에 관해 간단히 말하자면
영속 상태 : 영속성 컨텍스트가 자동으로 해당 엔티티를 영속화해주는 것
준영속 상태 : 영속성 컨텍스트의 관리 밖에 있는 상태
다른 내용 없이 정말 간단하게 설명하면 준영속 상태이면 값을 바꾸든 뭘 하든 영속성 컨텍스트가 관리하지 않으므로 실제로 적용이 되지 않는 상태라고 생각하면 편하다.
이 때 준영속 상태를 -> 영속 상태로 변경하는 두 가지 방법 중 하나가 바로 merge()이다.
@PutMapping("/user/{id}")
public void update(@PathVariable Long id, @RequestBody User requestUser) {
User user = userRepository.findById(id).orElseThrow(() -> {
// IllegalArgumentException 예외 처리
throw new IllegalArgumentException("해당하는 아이디가 없습니다 id : " + id);
});
user.setPassword(requestUser.getPassword());
user.setEmail(requestUser.getEmail());
userRepository.save(user); //엔티티를 수정한 후 명시적으로 save함수 실행
}
merge의 동작 방식은 다음과 같다.
이 방식에는 한 가지 큰 문제점이 있다.
바로 준영속 상태의 값들 중 null인 값이 있다면 기존의 데이터에서도 그게 null이 된다는 것!!
예를 들어
이름 나이 키
류찬 26 178
으로 이미 저장되어 있고, 이 사람이 키가 커서 183이 되었다고 가정했을 때
키만 183으로 설정한 엔티티를 merge하면
이름 나이 키
null null 183
이 될 수도 있다는 것이다!!
@Transactional //명시적인 save함수 호출 대신 @Transcational을 통해 entity가 수정되면 dirty checking으로 update.
@PutMapping("/user/{id}")
public void update(@PathVariable Long id, @RequestBody User requestUser) {
User user = userRepository.findById(id).orElseThrow(() -> {
// IllegalArgumentException 예외 처리
throw new IllegalArgumentException("해당하는 아이디가 없습니다 id : " + id);
});
user.setPassword(requestUser.getPassword());
user.setEmail(requestUser.getEmail());
}
위의 Merge 방식과는 다르게 save함수를 사용하지 않고 대신 함수에 @Transactional어노테이션을 붙여주었다.
함수나 클래스에 @Transactional 어노테이션을 붙여주면 해당 함수나 클래스가 Transaction이 되는데 Transaction을 간단히 설명하면
JPA는 엔티티를 조회할때 해당 엔티티의 조회 상태 그대로 스냅샷을 만들어 놓고 Transaction이 끝나는 시점에는 이 스냅샷과 비교해 다른점이 있으면 Update 쿼리를 실행한다.