트랜잭션 예시

박찬우·2024년 2월 1일

스프링 DB

목록 보기
13/53

리포지토리

  • 커넥션을 서비스에서 얻어와서 수행한다
public Member findById(Connection con, String memberId) throws SQLException {
	String sql = "select * from member where member_id = ?";

	PreparedStatement pstmt = null;
	ResultSet rs = null;

	try {
		pstmt = con.prepareStatement(sql);
		pstmt.setString(1, memberId);

		rs = pstmt.executeQuery();
		if(rs.next()) {
			Member member = new Member();
			member.setMemberId(rs.getString("member_id"));
			member.setMoney(rs.getInt("money"));
			return member;
		} else {
			throw new NoSuchElementException("member not found memberId=" + memberId);
		}

	} catch(SQLException e) {
		log.error("db error", e);
		throw e;
	} finally {
         // 다른 곳에서 커넥션을 사용해야 하기 때문에 커넥션을 종료하지 않음
		JdbcUtils.closeResultSet(rs);
		JdbcUtils.closeStatement(pstmt);
	}
}

public void update(Connection con, String memberId, int money) throws SQLException {
	String sql = "update member set money=? where member_id =?";

	PreparedStatement pstmt = null;

	try {
		pstmt = con.prepareStatement(sql);
		pstmt.setInt(1, money);
		pstmt.setString(2, memberId);
		pstmt.executeUpdate();
	} catch(SQLException e) {
		log.error("db error", e);
		throw e;
	} finally {
         // 다른 곳에서 커넥션을 사용해야 하기 때문에 커넥션을 종료하지 않음
		JdbcUtils.closeStatement(pstmt);
	}

}

서비스

  • 커넥션 하나를 파라미터로 전달하여 같은 트랜잭션안에서 처리되도록 한다
  • 로직 수행 중 예외 발생 시 트랜잭션 롤백을 실행한다
@Slf4j
@RequiredArgsConstructor
public class MemberServiceV2 {
    private final DataSource dataSource;
    private final MemberRepositoryV2 memberRepositoryV2;

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        Connection con = dataSource.getConnection();

        try{
            // 트랜잭션 시작
            con.setAutoCommit(false);

            // 비즈니스 로직
            Member fromMember = memberRepositoryV2.findById(con, fromId);
            Member toMember = memberRepositoryV2.findById(con, toId);

            memberRepositoryV2.update(con, fromId, fromMember.getMoney() - money);
            if(toMember.getMemberId().equals("ex")) {
                throw new IllegalStateException("이체중 예외 발생");
            }

            memberRepositoryV2.update(con, toId, toMember.getMoney() + money);

            // 트랜잭션 종료(커밋)
            con.commit();
        } catch (Exception e) {
            // 트랜잭션 종료(롤백)
            con.rollback();
            throw new IllegalStateException(e);
        } finally {
            if(con != null) {
                try {
                    // 커넥션 풀을 사용할 경우 수동 커밋 모드로 반납할 경우 수동 커밋 모드로 반납이 된다.
                    // 그래서 자동 커밋으로 변경 후 풀에 반납함
                    con.setAutoCommit(true);
                    con.close();
                } catch (Exception e) {
                    log.info("error", e);
                }
            }
        }
    }
}

TEST

@Test
@DisplayName("정상 이체")
void accountTransfer() throws SQLException {
	Member memberA = new Member(MEMBER_A, 10000);
	Member memberB = new Member(MEMBER_B, 10000);
	memberRepository.save(memberA);
	memberRepository.save(memberB);

	memberService.accountTransfer(memberA.getMemberId(), memberB.getMemberId(), 2000);

	Member findMemberA = memberRepository.findById(memberA.getMemberId());
	Member findMemberB = memberRepository.findById(memberB.getMemberId());
	Assertions.assertThat(findMemberA.getMoney()).isEqualTo(8000);
	Assertions.assertThat(findMemberB.getMoney()).isEqualTo(12000);
}

@Test
@DisplayName("이체중 예외 발생")
void accountTransferEx() throws SQLException {
	Member memberA = new Member(MEMBER_A, 10000);
	Member memberEx = new Member(MEMBER_EX, 10000);
	memberRepository.save(memberA);
	memberRepository.save(memberEx);

    // memberA가 memberEx로 2000원을 이체하여 memberA의 잔액이 10000원에서 8000원으로 변경됨
    // 예외 발생 시 트랜잭션이 롤백되어 memberA의 잔액이 8000원에서 10000원으로 돌아옴
	Assertions.assertThatThrownBy(() -> memberService.accountTransfer(memberA.getMemberId(), memberEx.getMemberId(), 2000))
			.isInstanceOf(IllegalStateException.class);

	Member findMemberA = memberRepository.findById(memberA.getMemberId());
	Member findMemberEx = memberRepository.findById(memberEx.getMemberId());
	Assertions.assertThat(findMemberA.getMoney()).isEqualTo(10000);
	Assertions.assertThat(findMemberEx.getMoney()).isEqualTo(10000);
}
profile
진짜 개발자가 되어보자

0개의 댓글