- @Transaction and programmatic using the same example case.
- XML format
This commit is contained in:
Rudi Adianto 2018-08-29 14:45:57 +07:00
parent 33b65622a5
commit 9325f26b24
8 changed files with 144 additions and 133 deletions

View File

@ -0,0 +1,5 @@
package com.baeldung.jta;
public class AuditService {
}

View File

@ -1,8 +1,10 @@
package com.baeldung.jtademo.services;
import com.baeldung.jtademo.dto.TransferLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@ -18,8 +20,14 @@ public class AuditService {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional
public void log(String fromAccount, String toAccount, BigDecimal amount) {
jdbcTemplate.update("insert into AUDIT_LOG(FROM_ACCOUNT, TO_ACCOUNT, AMOUNT) values ?,?,?", fromAccount, toAccount, amount);
}
public TransferLog lastTransferLog() {
return jdbcTemplate.query("select FROM_ACCOUNT,TO_ACCOUNT,AMOUNT from AUDIT_LOG order by ID desc", (ResultSetExtractor<TransferLog>) (rs) -> {
if(!rs.next()) return null;
return new TransferLog(rs.getString(1), rs.getString(2), BigDecimal.valueOf(rs.getDouble(3)));
});
}
}

View File

@ -1,27 +0,0 @@
package com.baeldung.jtademo.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.transaction.UserTransaction;
import java.math.BigDecimal;
@Component
public class BankAccountManualTxService {
@Resource
UserTransaction userTransaction;
@Autowired
@Qualifier("jdbcTemplateAccount")
JdbcTemplate jdbcTemplate;
public void transfer(String fromAccountId, String toAccountId, BigDecimal amount) throws Exception {
userTransaction.begin();
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE-? where ID=?", amount, fromAccountId);
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE+? where ID=?", amount, toAccountId);
userTransaction.commit();
}
}

View File

@ -3,6 +3,7 @@ package com.baeldung.jtademo.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@ -18,9 +19,15 @@ public class BankAccountService {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional
public void transfer(String fromAccountId, String toAccountId, BigDecimal amount) {
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE-? where ID=?", amount, fromAccountId);
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE+? where ID=?", amount, toAccountId);
}
public BigDecimal balanceOf(String accountId) {
return jdbcTemplate.query("select BALANCE from ACCOUNT where ID=?", new Object[] { accountId }, (ResultSetExtractor<BigDecimal>) (rs) -> {
rs.next();
return new BigDecimal(rs.getDouble(1));
});
}
}

View File

@ -4,29 +4,42 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import javax.transaction.UserTransaction;
import java.math.BigDecimal;
@Service
public class TellerService {
private final BankAccountService bankAccountService;
private final AuditService auditService;
private final UserTransaction userTransaction;
@Autowired
public TellerService(BankAccountService bankAccountService, AuditService auditService) {
public TellerService(BankAccountService bankAccountService, AuditService auditService, UserTransaction userTransaction) {
this.bankAccountService = bankAccountService;
this.auditService = auditService;
this.userTransaction = userTransaction;
}
@Transactional
public void executeTransfer(String fromAccontId, String toAccountId, BigDecimal amount) {
bankAccountService.transfer(fromAccontId, toAccountId, amount);
auditService.log(fromAccontId, toAccountId, amount);
BigDecimal balance = bankAccountService.balanceOf(fromAccontId);
if(balance.compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("Insufficient fund.");
}
}
@Transactional
public void executeTransferFail(String fromAccontId, String toAccountId, BigDecimal amount) {
public void executeTransferProgrammaticTx(String fromAccontId, String toAccountId, BigDecimal amount) throws Exception {
userTransaction.begin();
bankAccountService.transfer(fromAccontId, toAccountId, amount);
auditService.log(fromAccontId, toAccountId, amount);
throw new RuntimeException("Something wrong, rollback!");
BigDecimal balance = bankAccountService.balanceOf(fromAccontId);
if(balance.compareTo(BigDecimal.ZERO) <= 0) {
userTransaction.rollback();
throw new RuntimeException("Insufficient fund.");
} else {
userTransaction.commit();
}
}
}

View File

@ -43,17 +43,5 @@ public class TestHelper {
}
}
public TransferLog lastTransferLog() {
return jdbcTemplateAudit.query("select FROM_ACCOUNT,TO_ACCOUNT,AMOUNT from AUDIT_LOG order by ID desc", (ResultSetExtractor<TransferLog>) (rs) -> {
if(!rs.next()) return null;
return new TransferLog(rs.getString(1), rs.getString(2), BigDecimal.valueOf(rs.getDouble(3)));
});
}
public BigDecimal balanceOf(String accountId) {
return jdbcTemplateAccount.query("select BALANCE from ACCOUNT where ID=?", new Object[] { accountId }, (ResultSetExtractor<BigDecimal>) (rs) -> {
rs.next();
return new BigDecimal(rs.getDouble(1));
});
}
}

View File

@ -1,9 +1,7 @@
package com.baeldung.jtademo;
import com.baeldung.jtademo.dto.TransferLog;
import com.baeldung.jtademo.services.BankAccountManualTxService;
import com.baeldung.jtademo.services.TellerService;
import com.baeldung.jtademo.services.TestHelper;
import com.baeldung.jtademo.services.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -25,7 +23,10 @@ public class JtaDemoUnitTest {
TellerService tellerService;
@Autowired
BankAccountManualTxService bankAccountManualTxService;
BankAccountService accountService;
@Autowired
AuditService auditService;
@Before
public void beforeTest() throws Exception {
@ -34,14 +35,13 @@ public class JtaDemoUnitTest {
}
@Test
public void whenNoException_thenAllCommitted() throws Exception {
public void givenAnnotationTx_whenNoException_thenAllCommitted() throws Exception {
tellerService.executeTransfer("a0000001", "a0000002", BigDecimal.valueOf(500));
BigDecimal result = testHelper.balanceOf("a0000001");
assertThat(testHelper.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(500));
assertThat(testHelper.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2500));
assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(500));
assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2500));
TransferLog lastTransferLog = testHelper.lastTransferLog();
TransferLog lastTransferLog = auditService.lastTransferLog();
assertThat(lastTransferLog).isNotNull();
assertThat(lastTransferLog.getFromAccountId()).isEqualTo("a0000001");
assertThat(lastTransferLog.getToAccountId()).isEqualTo("a0000002");
@ -49,22 +49,39 @@ public class JtaDemoUnitTest {
}
@Test
public void whenException_thenAllRolledBack() throws Exception {
public void givenAnnotationTx_whenException_thenAllRolledBack() throws Exception {
assertThatThrownBy(() -> {
tellerService.executeTransferFail("a0000002", "a0000001", BigDecimal.valueOf(100));
}).hasMessage("Something wrong, rollback!");
tellerService.executeTransfer("a0000002", "a0000001", BigDecimal.valueOf(100000));
}).hasMessage("Insufficient fund.");
assertThat(testHelper.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000));
assertThat(testHelper.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000));
assertThat(testHelper.lastTransferLog()).isNull();
assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000));
assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000));
assertThat(auditService.lastTransferLog()).isNull();
}
@Test
public void givenBMT_whenNoException_thenAllCommitted() throws Exception {
bankAccountManualTxService.transfer("a0000001", "a0000002", BigDecimal.valueOf(100));
public void givenProgrammaticTx_whenCommit_thenAllCommitted() throws Exception {
tellerService.executeTransferProgrammaticTx("a0000001", "a0000002", BigDecimal.valueOf(500));
assertThat(testHelper.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(900));
assertThat(testHelper.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2100));
BigDecimal result = accountService.balanceOf("a0000001");
assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(500));
assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2500));
TransferLog lastTransferLog = auditService.lastTransferLog();
assertThat(lastTransferLog).isNotNull();
assertThat(lastTransferLog.getFromAccountId()).isEqualTo("a0000001");
assertThat(lastTransferLog.getToAccountId()).isEqualTo("a0000002");
assertThat(lastTransferLog.getAmount()).isEqualByComparingTo(BigDecimal.valueOf(500));
}
@Test
public void givenProgrammaticTx_whenRollback_thenAllRolledBack() throws Exception {
assertThatThrownBy(() -> {
tellerService.executeTransferProgrammaticTx("a0000002", "a0000001", BigDecimal.valueOf(100000));
}).hasMessage("Insufficient fund.");
assertThat(accountService.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000));
assertThat(accountService.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000));
assertThat(auditService.lastTransferLog()).isNull();
}
}