우당탕탕 개발일지

[SpringBoot] 5. 트랜잭션 (Service 계층) 본문

Server

[SpringBoot] 5. 트랜잭션 (Service 계층)

devchop 2025. 1. 6. 16:50

 

트랜잭션

Service계층이 해야하는 역할, 트랜잭션이다.

연관된 작업은 모두 성공하거나 모두 실패한다. 이 쪼갤 수 없는 작업의 단위를 트랜잭션이라고 한다. 

start transaction;
commit; ## success
rollback; ## fail

 

서비스 메소드가 시작할 때 트랜잭션이 시작된다
서비스 메소드 로직이 모두 성공적이면 commit,
한개라도 실패하면 rollback되도록 구현해보자.

Service 계층의 함수 위에  @Transaction 어노테이션을 이용하여 구현할 수 있다.

    @Transactional
    public void saveUser(UserCreateRequest request){
        userRepository.save(new User(request.getName(), request.getAge()));
    }

주의할점은, IOException은 Java의 체크예외이기 때문에 트랜잭션 안에서 발생하더라도 롤백되지않는다.

 

 

영속성 컨텍스트

테이블과 매핑된 Entity 객체를 관리, 보관하는 역할을 담당한다.
스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생기고, 트랜잭션이 종료되면 영속성 컨텍스트도 종료된다. 역할은 다음과 같다.

  • 변경감지(dirty check) : 영속성 컨테이너에 불러와진 entity는 명시적으로 repository.save()를 하지 않더라도 변경을 감지해서 자동으로 저장된다.
 @Transactional
    public void updateUser(UserUpdateRequest request){

        User user =  userRepository.findById(request.getId()).orElseThrow(()->new IllegalArgumentException("invalid id"));
        user.UpdateName(request.getName()); //user의 변경이 감지되었다.
        //save()를 하지 않더라도 자동으로 저장된다.
        //userRepository.save(user); 
    }
  • 쓰기지연: db의 명령어들 insert, update, delete등을 바로바로 날리는 것이 아니라 한번에 날린다.
    묶여있는 모든 명령어에 문제가 없는지를 확인한 후, commit상태일 때 한번에 sql명령어를 날린다.
  • 1차캐싱 : id를 기준으로 entity를 기억한다. 이렇게 캐싱된 객체 (user1,user2,user3) 은 완전 동일한 인스턴스이다.
@Transactional(readOnly = true)
public void findUsers(){
    User user1 = userRepository.findById(1).get();
    User user2 = userRepository.findById(1).get(); //id = 1 인 user를 기억하고있으므로 새로찾지 않음.
    User user3 = userRepository.findById(1).get(); //id = 1 인 user를 기억하고있으므로 새로찾지 않음.
}