애플리케이션에서 DB 트랜잭션을 적용하기
적용해보기 전 비교를 위해 트랜잭션을 적용하지 않고 진행
트랜잭션이 적용되지 않은 MemberServiceV1
@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("이체중 예외 발생");
}
}
}
이체중 예외발생 테스트 코드
@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);
assertThatThrownBy(() -> memberService.accountTransfer(memberA.getMemberId(), memberEx.getMemberId(), 2000))
.isInstanceOf(IllegalStateException.class);
Member findMemberA = memberRepository.findById(memberA.getMemberId());
Member findMemberEx = memberRepository.findById(memberEx.getMemberId());
assertThat(findMemberA.getMoney()).isEqualTo(8000);
assertThat(findMemberEx.getMoney()).isEqualTo(10000);
}
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.info("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.info("db error",e);
throw e;
}finally {
JdbcUtils.closeStatement(pstmt);
}
}
트랜잭션 연동 로직 추가
@Slf4j
@RequiredArgsConstructor
public class MemberServiceV2 {
private final DataSource dataSource;
private final MemberRepositoryV2 memberRepository;
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
Connection con = dataSource.getConnection();
try{
//트랜잭션 시작을 위해서 자동커밋 끄기.
con.setAutoCommit(false);
bizLogic(con,fromId, toId, money);
con.commit();
}catch (Exception e){
con.rollback();
throw new IllegalStateException(e);
}finally {
release(con);
}
}
private void bizLogic(Connection con, String fromId, String toId, int money) throws SQLException {
Member fromMember = memberRepository.findById(con,fromId);
Member toMember = memberRepository.findById(con,toId);
memberRepository.update(con,fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(con,toId, toMember.getMoney() + money);
}
private void validation(Member toMember) {
if (toMember.getMemberId().equals("ex")) {
throw new IllegalStateException("이체중 예외 발생");
}
}
private void release(Connection con){
if(con != null){
try{
con.setAutoCommit(true);
con.close();
}catch (Exception e){
log.info("error",e);
}
}
}
}