// 트랜잭션 시작
val status: TransactionStatus = transactionManager.getTransaction(DefaultTransactionDefinition())
try {
bizLogic(fromId, toId, money)
transactionManager.commit(status) // 성공 시 커밋
} catch (e: Exception){
transactionManager.rollback(status) // 실패 시 롤백
throw IllegalStateException(e)
}
public class TransactionTemplate {
private PlatformTransactionManager transactionManager;
public <T> T execute(TransactionCallback<T> action){..}
void executeWithoutResult(Consumer<TransactionStatus> action){..}
}
트랜잭션 템플릿을 사용해서 반복하는 부분을 제거해보자
package hello.jbdc.service
import hello.jbdc.Log
import hello.jbdc.domain.Member
import hello.jbdc.repository.MemberRepositoryV3
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.DefaultTransactionDefinition
import org.springframework.transaction.support.TransactionTemplate
import java.sql.Connection
import java.sql.SQLException
import kotlin.jvm.Throws
/*
트랜잭션 - 트랜잭션 템플릿
*/
class MemberServiceV3_2(
private val transactionManager: PlatformTransactionManager,
private val memberRepository: MemberRepositoryV3
) :Log{
val txTemplate: TransactionTemplate
init{
txTemplate = TransactionTemplate(transactionManager)
}
@Throws(SQLException::class)
fun accountTransfer(fromId: String, toId: String, money: Int){
// 트랜잭션 시작
txTemplate.executeWithoutResult {
try{
bizLogic(fromId, toId, money)
} catch (e: SQLException){
throw IllegalStateException(e)
}
}
}
@Throws(SQLException::class)
private fun bizLogic(fromId: String, toId: String, money: Int){
val fromMember = memberRepository.findById(fromId)
val toMember = memberRepository.findById(toId)
memberRepository.update(fromId, fromMember.money - money)
validation(toMember)
memberRepository.update(toId, fromMember.money + money)
}
private fun validation(toMember: Member){
if (toMember.memberId.equals("ex")){
throw IllegalStateException("이체 중 예외 발생")
}
}
}
txTemplate.executeWithoutResult {
try{
bizLogic(fromId, toId, money)
} catch (e: SQLException){
throw IllegalStateException(e) // 언체크 예외
}
}
package hello.jbdc.service
import hello.jbdc.connection.ConnectionConst
import hello.jbdc.domain.Member
import hello.jbdc.repository.MemberRepositoryV3
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.jdbc.datasource.DataSourceTransactionManager
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import java.sql.SQLException
internal class MemberServiceV3_2Test{
val MEMBER_A = "memberA"
val MEMBER_B = "memberB"
val MEMBER_EX = "ex"
lateinit var memberRepository: MemberRepositoryV3
lateinit var memberService: MemberServiceV3_2
@BeforeEach
fun before(){
val dataSource = DriverManagerDataSource(ConnectionConst.URL,
ConnectionConst.USERNAME, ConnectionConst.PASSWORD)
val transactionManager: PlatformTransactionManager = DataSourceTransactionManager(dataSource)
memberRepository = MemberRepositoryV3(dataSource)
memberService = MemberServiceV3_2(transactionManager, memberRepository)
}
@AfterEach
@Throws(SQLException::class)
fun after(){
memberRepository.delete(MEMBER_A);
memberRepository.delete(MEMBER_B);
memberRepository.delete(MEMBER_EX);
}
@Test
@DisplayName("정상 이체")
@Throws(SQLException::class)
fun accountTransfer() {
//given
val memberA = Member(MEMBER_A, 10000)
val memberB = Member(MEMBER_B, 10000)
memberRepository.save(memberA)
memberRepository.save(memberB)
//when
memberService.accountTransfer(memberA.memberId,
memberB.memberId, 2000)
// //then
val findMemberA: Member = memberRepository.findById(memberA.memberId)
val findMemberB: Member = memberRepository.findById(memberB.memberId)
assertThat(findMemberA.money).isEqualTo(8000)
assertThat(findMemberB.money).isEqualTo(12000)
}
@Test
@DisplayName("이체 중 예외 발생")
fun accountTransferEx(){
//given
val memberA = Member(MEMBER_A, 10000)
val memberEx = Member(MEMBER_EX, 10000)
memberRepository.save(memberA)
memberRepository.save(memberEx)
// when
assertThatThrownBy {
memberService.accountTransfer(memberA.memberId, memberEx.memberId,
2000)
}
.isInstanceOf(IllegalStateException::class.java)
// then
val findMemberA = memberRepository.findById(memberA.memberId)
val findMemberEx = memberRepository.findById(memberEx.memberId)
assertThat(findMemberA.money).isEqualTo(10000)
assertThat(findMemberEx.money).isEqualTo(10000)
}
}