티스토리 뷰
스프링은 AOP안에서 자기 자신의 메서드를 호출하면 AOP가 적용되지 않음.
정말 기본적인 상식?이지만 깜빡하는 경우가 있음.
외부에서 호출해야 해당 @Transactional이 존재하는 빈을 감싼 proxy bean이 호출되어 Transaction이 적용됨.
실제로 적용이 안되는 지 간단한 테스트를 해보자.
import jpabook.jpashop.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@Autowired
TestService testService;
@GetMapping("/test")
public String test() {
testService.join();
return "hello";
}
}
위는 간단한 컨트롤러로 hello로 들어오게 되면 testService의 join메소드를 호출하게 됨.
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TestService {
@Autowired
MemberRepository memberRepository;
@Transactional
public void join() {
Member member = new Member();
member.setName("join");
memberRepository.save(member);
test();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test() {
Member member = new Member();
member.setName("requires_new");
memberRepository.save(member);
}
}
TestService의 join 메소드를 보면 Member 인스턴스를 생성해서 db에 insert를 하는데, 중간에 동일한 TestService의 내부 함수인 test 메소드를 호출한 뒤, RuntimeException을 던지면 어떻게 될까?
test 메소드의 경우 Propagation required_new로 무조건 새로운 트랜잭션을 생성하도록 처리하였기 때문에, 만약 트랜잭션이 정상적으로 모두 적용되었다면 (join과 test 둘 다) db에는 requires_new의 이름을 가진 member 데이터는 정상적으로 insert되었을 것이고, join의 이름을 가진 member 데이터는 rollback되었을 것임.
test url을 호출하여 Exception이 발생시킨 뒤 데이터를 확인해보면, 모두 롤백되어 아무 데이터도 없는 것을 확인할 수 있음.
즉, test 메소드는 내부에서 호출하였기 때문에 트랜잭션이 따로 적용되지 않고, 그대로 join 메소드의 트랜잭션에 포함되어 적용이 된 것.
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class Test2Service {
@Autowired
MemberRepository memberRepository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test() {
Member member = new Member();
member.setName("requires_new");
memberRepository.save(member);
}
}
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TestService {
@Autowired
MemberRepository memberRepository;
@Autowired
Test2Service test2Service;
@Transactional
public void join() {
Member member = new Member();
member.setName("join");
memberRepository.save(member);
// test2Service의 test를 호출하도록 수정
test2Service.test();
throw new RuntimeException();
}
}
이를 수정해서 test 메소드를 다른 서비스가 갖도록 하고, 그 서비스를 통해서 test 메소드를 호출하게 되면, 이전에 기대했던 결과 그대로 데이터가 생성됨을 알 수 있음.
join member는 rollback되고, requires_new는 그대로 commit된 것을 볼 수 있음.
- Total
- Today
- Yesterday
- 프로그래머스 Level 1
- 비동기
- blocking
- 필터
- Synchronous
- Filter
- 프로그래머스
- http://www.nextree.co.kr/p6960/
- 해시
- 인터셉터
- 스택/큐
- Asynchronous
- 동기
- 논블로킹
- 프로그래머스 Level 3
- Handler Interceptor
- a
- 프로그래머스 Level 2
- non-blocking
- 핸들러 인터셉터
- 블로킹
- 코딩테스트 고득점 Kit
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |