BAEL-2081
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package com.baeldung.jtademo;
|
||||
|
||||
import com.baeldung.jtademo.services.TellerService;
|
||||
import org.hsqldb.jdbc.JDBCDataSourceFactory;
|
||||
import org.hsqldb.jdbc.pool.JDBCXADataSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.jta.bitronix.BitronixXADataSourceWrapper;
|
||||
import org.springframework.boot.jta.bitronix.PoolingDataSourceBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableTransactionManagement
|
||||
public class JtaDemoApplication {
|
||||
|
||||
@Bean("dataSourceAccount")
|
||||
public DataSource dataSource() throws Exception {
|
||||
return createHsqlXADatasource("jdbc:hsqldb:mem:accountDb");
|
||||
}
|
||||
|
||||
@Bean("dataSourceAudit")
|
||||
public DataSource dataSourceAudit() throws Exception {
|
||||
return createHsqlXADatasource("jdbc:hsqldb:mem:auditDb");
|
||||
}
|
||||
|
||||
private DataSource createHsqlXADatasource(String connectionUrl) throws Exception {
|
||||
JDBCXADataSource dataSource = new JDBCXADataSource();
|
||||
dataSource.setUrl(connectionUrl);
|
||||
dataSource.setUser("sa");
|
||||
BitronixXADataSourceWrapper wrapper = new BitronixXADataSourceWrapper();
|
||||
return wrapper.wrapDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean("jdbcTemplateAccount")
|
||||
public JdbcTemplate jdbcTemplate(@Qualifier("dataSourceAccount") DataSource dataSource) {
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Bean("jdbcTemplateAudit")
|
||||
public JdbcTemplate jdbcTemplateAudit(@Qualifier("dataSourceAudit") DataSource dataSource) {
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.baeldung.jtademo.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class TransferLog {
|
||||
private String fromAccountId;
|
||||
private String toAccountId;
|
||||
private BigDecimal amount;
|
||||
|
||||
public TransferLog(String fromAccountId, String toAccountId, BigDecimal amount) {
|
||||
this.fromAccountId = fromAccountId;
|
||||
this.toAccountId = toAccountId;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getFromAccountId() {
|
||||
return fromAccountId;
|
||||
}
|
||||
|
||||
public String getToAccountId() {
|
||||
return toAccountId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
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.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Service
|
||||
public class AuditService {
|
||||
|
||||
final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public AuditService(@Qualifier("jdbcTemplateAudit") JdbcTemplate jdbcTemplate) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Service
|
||||
public class BankAccountService {
|
||||
|
||||
final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public BankAccountService(@Qualifier("jdbcTemplateAccount") JdbcTemplate jdbcTemplate) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.baeldung.jtademo.services;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Service
|
||||
public class TellerService {
|
||||
private final BankAccountService bankAccountService;
|
||||
private final AuditService auditService;
|
||||
|
||||
@Autowired
|
||||
public TellerService(BankAccountService bankAccountService, AuditService auditService) {
|
||||
this.bankAccountService = bankAccountService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void executeTransfer(String fromAccontId, String toAccountId, BigDecimal amount) {
|
||||
bankAccountService.transfer(fromAccontId, toAccountId, amount);
|
||||
auditService.log(fromAccontId, toAccountId, amount);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void executeTransferFail(String fromAccontId, String toAccountId, BigDecimal amount) {
|
||||
bankAccountService.transfer(fromAccontId, toAccountId, amount);
|
||||
auditService.log(fromAccontId, toAccountId, amount);
|
||||
throw new RuntimeException("Something wrong, rollback!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@Component
|
||||
public class TestHelper {
|
||||
final JdbcTemplate jdbcTemplateAccount;
|
||||
|
||||
final JdbcTemplate jdbcTemplateAudit;
|
||||
|
||||
@Autowired
|
||||
public TestHelper(@Qualifier("jdbcTemplateAccount") JdbcTemplate jdbcTemplateAccount, @Qualifier("jdbcTemplateAudit") JdbcTemplate jdbcTemplateAudit) {
|
||||
this.jdbcTemplateAccount = jdbcTemplateAccount;
|
||||
this.jdbcTemplateAudit = jdbcTemplateAudit;
|
||||
}
|
||||
|
||||
public void runAccountDbInit() throws SQLException {
|
||||
runScript("account.sql", jdbcTemplateAccount.getDataSource());
|
||||
}
|
||||
|
||||
public void runAuditDbInit() throws SQLException {
|
||||
runScript("audit.sql", jdbcTemplateAudit.getDataSource());
|
||||
}
|
||||
|
||||
private void runScript(String scriptName, DataSource dataSouorce) throws SQLException {
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Resource script = resourceLoader.getResource(scriptName);
|
||||
try (Connection con = dataSouorce.getConnection()) {
|
||||
ScriptUtils.executeSqlScript(con, script);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
DROP SCHEMA PUBLIC CASCADE;
|
||||
|
||||
create table ACCOUNT (
|
||||
ID char(8) PRIMARY KEY,
|
||||
BALANCE NUMERIC(28,10)
|
||||
);
|
||||
|
||||
insert into ACCOUNT(ID, BALANCE) values ('a0000001', 1000);
|
||||
insert into ACCOUNT(ID, BALANCE) values ('a0000002', 2000);
|
||||
@@ -0,0 +1,8 @@
|
||||
DROP SCHEMA PUBLIC CASCADE;
|
||||
|
||||
create table AUDIT_LOG (
|
||||
ID INTEGER IDENTITY PRIMARY KEY,
|
||||
FROM_ACCOUNT varchar(8),
|
||||
TO_ACCOUNT varchar(8),
|
||||
AMOUNT numeric(28,10)
|
||||
);
|
||||
Reference in New Issue
Block a user