먼저 트랜잭션 적용 전(오토 커밋 모드)
@RequiredArgsConstructor
public class MemberServiceV1 {
private final MemberRepositoryV1 memberRepository;
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
Member fromMember = memberRepository.findById(fromId);
Member toMember = memberRepository.findById(toId);
memberRepository.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(toId, toMember.getMoney() + money);
}
private void validation(Member toMember) {
if(toMember.getMemberId().equals("ex")){
throw new IllegalStateException("이체 중 예외 발생");
}
}
}
송금 id, 수금 id, 금액 받아서 DB에 반영하는 로직.
먼저 DB에서 각 계정 정보를 찾은 다음에 그 계정이 가지고 있던 돈에서 update하는 식으로.
validation은 그냥 멤버id가 ex면 예외를 뿌리는 거임.
억지로 뿌리게 만들었음. 테스트를 위해.
public class MemberServiceV1Test {
private static final String MEMBER_A = "memberA";
private static final String MEMBER_B = "memberB";
private static final String MEMBER_EX = "ex";
private MemberRepositoryV1 memberRepository;
private MemberServiceV1 memberService;
@BeforeEach
private void before(){
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
memberRepository = new MemberRepositoryV1(dataSource);
memberService = new MemberServiceV1(memberRepository);
}
@AfterEach
private void after() throws SQLException {
memberRepository.delete(MEMBER_A);
memberRepository.delete(MEMBER_B);
memberRepository.delete(MEMBER_EX);
}
@Test
@DisplayName("정상 이체")
void accountTransfer() throws SQLException {
Member memberA = new Member(MEMBER_A, 10000);
Member memberB = new Member(MEMBER_B, 10000);
int transferMoney = 2000;
memberRepository.save(memberA);
memberRepository.save(memberB);
memberService.accountTransfer(memberA.getMemberId(), memberB.getMemberId(), transferMoney);
Member findedMemberA = memberRepository.findById(memberA.getMemberId());
Member findedMemberB = memberRepository.findById(memberB.getMemberId());
assertThat(findedMemberA.getMoney()).isEqualTo(memberA.getMoney() - transferMoney);
assertThat(findedMemberB.getMoney()).isEqualTo(memberB.getMoney() + transferMoney);
}
@Test
@DisplayName("이체 중 예외 발생")
void accountTransferEx() throws SQLException {
Member memberA = new Member(MEMBER_A, 10000);
Member memberB = new Member(MEMBER_EX, 10000);
int transferMoney = 2000;
memberRepository.save(memberA);
memberRepository.save(memberB);
assertThatThrownBy(()->memberService.accountTransfer(memberA.getMemberId(), memberB.getMemberId(), transferMoney))
.isInstanceOf(IllegalStateException.class);
Member findedMemberA = memberRepository.findById(memberA.getMemberId());
Member findedMemberB = memberRepository.findById(memberB.getMemberId());
assertThat(findedMemberA.getMoney()).isEqualTo(memberA.getMoney() - transferMoney);
assertThat(findedMemberB.getMoney()).isEqualTo(memberB.getMoney());
}
}
처음에 BeforeEach에서 커넥션을 얻기 위한 DataSource를 얻어주고,
테스트 환경 이니셜라이징.
정상 이체의 경우,
멤버 생성, 초기자금 1만원, 등록.
서비스의 이체로직 수행.
정상 이체의 경우 MemberId가 ex가 아니므로 에러를 뿌리지 않고 정상수행하게 됨.
그 다음 다시 db에서 조회해서 각 member를 얻어옴.
그 조회해서 얻어온 멤버의 금액이 각자 맞게 되었는지 확인.
그 후 AfterEach에서는 DB에 등록했던 모든 MEMBER 다 지워서 원래 상태로 되돌림.
이 부분도 나중에 그냥 트랜잭션으로 롤백해버리면 된다고 함.
그럼 사용하는 DB의 세션이 같은 세션 이여야 하겠네.
이체 중 예외 발생은
이번엔 이체받을 대상의 멤버 이름을 ex로 설정.
이러면 Service에서 예외 뿌리도록 설정해 놨음.
assertThatThrownBy() 해서 나온 예외가 IllegalStateException 클래스의 인스턴스이면 정상적으로 이어서 작동.
service 단계에서 MEMBER_EX의 money를 update 하기 전에 예외를 던져서 그 service의 이체함수는 중단되고 예외가 올라와서, MEMBER_EX의 money에 제대로 반영이 안됨.
그래서 조회해서 찾았을 때도, MEMBER_A의 돈은 빠져나갔지만 MEBER_EX의 돈은 그대로 임.
문제가 심각함.
'스프링 > 5. 스프링 DB-1' 카테고리의 다른 글
22. 기존 트랜잭션의 문제 (0) | 2023.09.30 |
---|---|
21. 이체 트랜잭션 적용 (0) | 2023.09.30 |
19. 조회할 때 락 가져오기 (0) | 2023.09.30 |
18. DB 락 (0) | 2023.09.29 |
16. 자동커밋, 수동 커밋 (0) | 2023.09.29 |