JPA로 업데이트 시
memberRepository.save(member);
이런 방식으로 save() 메소드를 이용했었다.
이 save() 메소드를 찾아서 까보면
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
이렇게 생긴 것을 볼 수 있다.
여기서 봐야 할 것은 @Transactional 어노테이션이다.
우리가 save() 메소드를 호출할 때 저 어노테이션의 영향을 받는다.
이 @Transactional은 해당 어노테이션이 적용되는 곳에서 객체의 변경이 일어나면 자동으로 업데이트를 해준다.
@Transactional
public void updateMemberPassword(int pk,String password){
Member member = memberRepository.findMemberByMemberId(pk);
if(member == null){
throw new EntityNotFoundException();
}
member.updatePassword(password);
// memberRepository.save(member);
}
그래서 위와 같이 save를 지우고 @Transactional 어노테이션을 달아줘도 정상적으로 업데이트가 된다.
왜냐면 member 객체의 password값이 바뀌었고 이걸 Transactional이 값이 바뀌었다는 것을 알기 때문에 업데이트 쿼리가 발생한다.
그래서 값의 변경이 일어나지 않는다면 업데이트 쿼리는 발생하지 않고 위의 findMemberByMemberId로 인한 쿼리문만 발생한다.
추가로 저 @Transactional 어노테이션과 save() 메소드를 같이 사용해도 상관없다.
가독성이 좋고 일관성 있게 하기 위해 둘 다 사용하는 경우도 있다고 한다.
이런건 각자 스타일이나 프로젝트에서 어떻게 하는지? 회사에서 어떻게 하는지?에 초점을 맞추면 될 듯하다.
Transaction과 비 Transaction 나누기
말이 이상해서 이해가 안갈 것 같지만 말 그대로 Transaction 처리가 필요한 서비스와 필요없는 서비스를 구분하는 것이다.
이렇게 컨트롤러와 서비스 사이에 처리할 계층을 하나 추가하는 것이다.
컨트롤러에선 값의 검증, Application호출 후 해당 데이터를 이용하여 응답 내용을 만들것이다.
Application 에서는 Transaction이 필요없는 서비스를 처리하고 Transaction이 필요한 경우 Service를 호출할 것이다. 예를 들어 계좌 이체를 했을 때 계좌 이체를 했다고 메일을 발송하는 것이다.
Service 에서는 모두 Transaction이 필요한 코드들만 있을 것이다.
계좌 이체의 경우 내 계좌에서 돈이 빠지고 상대 계좌에는 돈이 입금되고 모두 한 번에 일어나야 한다.
만약 여기서 중간에 문제가 발생하여 롤백이 되면 그 이후에 보내야 하는 이체 성공 메일은 문제가 발생한 부분부터 코드 실행이 되지 않으니 당연히 발송되지 않을 것이고, 만약 성공했는데 메일 발송에 실패했다면 로그를 남겨 다시 재전송 하는 방식을 사용하면 서비스 사용에 있어서 큰 문제가 되지는 않을 것이다.
위와 같이 구분하면 서비스 규모가 커졌을 때 코드 관리도 쉬워지고 유지 보수에 있어 장점이 생긴다고 생각한다.
이렇게 기존에 작성하던 방식에서 문제점을 찾고 개선해 나가려 하지만 습관이 무서워 또 생각없이 작성할까봐 걱정이 된다.
'Java > Spring(Boot)' 카테고리의 다른 글
JPA와 ORM (0) | 2023.02.18 |
---|---|
이해 안돼서 다시 정리하는 IoC/DI (0) | 2023.02.07 |
Spring Annotation 정리 (0) | 2023.02.03 |
Spring Framework 개념 정리 (0) | 2023.02.02 |