Skip to content

Commit 9ca21b1

Browse files
authored
Merge pull request eugenp#5166 from bungrudi/BAEL-2081---Guide-to-JavaEE-JTA
Bael 2081 - Guide to Java EE JTA
2 parents 977c50a + 37daee1 commit 9ca21b1

File tree

12 files changed

+425
-0
lines changed

12 files changed

+425
-0
lines changed

jta/pom.xml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>com.baeldung</groupId>
6+
<artifactId>jta-demo</artifactId>
7+
<version>1.0-SNAPSHOT</version>
8+
<packaging>jar</packaging>
9+
10+
<description>JEE JTA demo</description>
11+
12+
<parent>
13+
<groupId>org.springframework.boot</groupId>
14+
<artifactId>spring-boot-starter-parent</artifactId>
15+
<version>2.0.4.RELEASE</version>
16+
<relativePath/>
17+
</parent>
18+
19+
<properties>
20+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
22+
<java.version>1.8</java.version>
23+
</properties>
24+
25+
<dependencies>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-jta-bitronix</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-jdbc</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter</artifactId>
37+
</dependency>
38+
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-test</artifactId>
42+
<scope>test</scope>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>org.hsqldb</groupId>
47+
<artifactId>hsqldb</artifactId>
48+
<version>2.4.1</version>
49+
</dependency>
50+
</dependencies>
51+
52+
<profiles>
53+
<profile>
54+
<id>autoconfiguration</id>
55+
<build>
56+
<plugins>
57+
<plugin>
58+
<groupId>org.apache.maven.plugins</groupId>
59+
<artifactId>maven-surefire-plugin</artifactId>
60+
<executions>
61+
<execution>
62+
<phase>integration-test</phase>
63+
<goals>
64+
<goal>test</goal>
65+
</goals>
66+
<configuration>
67+
<excludes>
68+
<exclude>**/*LiveTest.java</exclude>
69+
<exclude>**/*IntegrationTest.java</exclude>
70+
<exclude>**/*IntTest.java</exclude>
71+
</excludes>
72+
<includes>
73+
<include>**/AutoconfigurationTest.java</include>
74+
</includes>
75+
</configuration>
76+
</execution>
77+
</executions>
78+
<configuration>
79+
<systemPropertyVariables>
80+
<test.mime>json</test.mime>
81+
</systemPropertyVariables>
82+
</configuration>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
</profile>
87+
</profiles>
88+
</project>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.baeldung.jtademo;
2+
3+
import org.hsqldb.jdbc.pool.JDBCXADataSource;
4+
import org.springframework.beans.factory.annotation.Qualifier;
5+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6+
import org.springframework.boot.jta.bitronix.BitronixXADataSourceWrapper;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.ComponentScan;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.jdbc.core.JdbcTemplate;
11+
import org.springframework.transaction.annotation.EnableTransactionManagement;
12+
13+
import javax.sql.DataSource;
14+
15+
@EnableAutoConfiguration
16+
@EnableTransactionManagement
17+
@Configuration
18+
@ComponentScan
19+
public class JtaDemoApplication {
20+
21+
@Bean("dataSourceAccount")
22+
public DataSource dataSource() throws Exception {
23+
return createHsqlXADatasource("jdbc:hsqldb:mem:accountDb");
24+
}
25+
26+
@Bean("dataSourceAudit")
27+
public DataSource dataSourceAudit() throws Exception {
28+
return createHsqlXADatasource("jdbc:hsqldb:mem:auditDb");
29+
}
30+
31+
private DataSource createHsqlXADatasource(String connectionUrl) throws Exception {
32+
JDBCXADataSource dataSource = new JDBCXADataSource();
33+
dataSource.setUrl(connectionUrl);
34+
dataSource.setUser("sa");
35+
BitronixXADataSourceWrapper wrapper = new BitronixXADataSourceWrapper();
36+
return wrapper.wrapDataSource(dataSource);
37+
}
38+
39+
@Bean("jdbcTemplateAccount")
40+
public JdbcTemplate jdbcTemplate(@Qualifier("dataSourceAccount") DataSource dataSource) {
41+
return new JdbcTemplate(dataSource);
42+
}
43+
44+
@Bean("jdbcTemplateAudit")
45+
public JdbcTemplate jdbcTemplateAudit(@Qualifier("dataSourceAudit") DataSource dataSource) {
46+
return new JdbcTemplate(dataSource);
47+
}
48+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.baeldung.jtademo.dto;
2+
3+
import java.math.BigDecimal;
4+
5+
public class TransferLog {
6+
private String fromAccountId;
7+
private String toAccountId;
8+
private BigDecimal amount;
9+
10+
public TransferLog(String fromAccountId, String toAccountId, BigDecimal amount) {
11+
this.fromAccountId = fromAccountId;
12+
this.toAccountId = toAccountId;
13+
this.amount = amount;
14+
}
15+
16+
public String getFromAccountId() {
17+
return fromAccountId;
18+
}
19+
20+
public String getToAccountId() {
21+
return toAccountId;
22+
}
23+
24+
public BigDecimal getAmount() {
25+
return amount;
26+
}
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.baeldung.jtademo.services;
2+
3+
import com.baeldung.jtademo.dto.TransferLog;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.beans.factory.annotation.Qualifier;
6+
import org.springframework.jdbc.core.JdbcTemplate;
7+
import org.springframework.jdbc.core.ResultSetExtractor;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.math.BigDecimal;
11+
12+
@Service
13+
public class AuditService {
14+
15+
final JdbcTemplate jdbcTemplate;
16+
17+
@Autowired
18+
public AuditService(@Qualifier("jdbcTemplateAudit") JdbcTemplate jdbcTemplate) {
19+
this.jdbcTemplate = jdbcTemplate;
20+
}
21+
22+
public void log(String fromAccount, String toAccount, BigDecimal amount) {
23+
jdbcTemplate.update("insert into AUDIT_LOG(FROM_ACCOUNT, TO_ACCOUNT, AMOUNT) values ?,?,?", fromAccount, toAccount, amount);
24+
}
25+
26+
public TransferLog lastTransferLog() {
27+
return jdbcTemplate.query("select FROM_ACCOUNT,TO_ACCOUNT,AMOUNT from AUDIT_LOG order by ID desc", (ResultSetExtractor<TransferLog>) (rs) -> {
28+
if (!rs.next())
29+
return null;
30+
return new TransferLog(rs.getString(1), rs.getString(2), BigDecimal.valueOf(rs.getDouble(3)));
31+
});
32+
}
33+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.baeldung.jtademo.services;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.beans.factory.annotation.Qualifier;
5+
import org.springframework.jdbc.core.JdbcTemplate;
6+
import org.springframework.jdbc.core.ResultSetExtractor;
7+
import org.springframework.stereotype.Service;
8+
9+
import java.math.BigDecimal;
10+
11+
@Service
12+
public class BankAccountService {
13+
14+
final JdbcTemplate jdbcTemplate;
15+
16+
@Autowired
17+
public BankAccountService(@Qualifier("jdbcTemplateAccount") JdbcTemplate jdbcTemplate) {
18+
this.jdbcTemplate = jdbcTemplate;
19+
}
20+
21+
public void transfer(String fromAccountId, String toAccountId, BigDecimal amount) {
22+
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE-? where ID=?", amount, fromAccountId);
23+
jdbcTemplate.update("update ACCOUNT set BALANCE=BALANCE+? where ID=?", amount, toAccountId);
24+
}
25+
26+
public BigDecimal balanceOf(String accountId) {
27+
return jdbcTemplate.query("select BALANCE from ACCOUNT where ID=?", new Object[] { accountId }, (ResultSetExtractor<BigDecimal>) (rs) -> {
28+
rs.next();
29+
return new BigDecimal(rs.getDouble(1));
30+
});
31+
}
32+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.baeldung.jtademo.services;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.stereotype.Service;
5+
6+
import javax.transaction.Transactional;
7+
import javax.transaction.UserTransaction;
8+
import java.math.BigDecimal;
9+
10+
@Service
11+
public class TellerService {
12+
private final BankAccountService bankAccountService;
13+
private final AuditService auditService;
14+
private final UserTransaction userTransaction;
15+
16+
@Autowired
17+
public TellerService(BankAccountService bankAccountService, AuditService auditService, UserTransaction userTransaction) {
18+
this.bankAccountService = bankAccountService;
19+
this.auditService = auditService;
20+
this.userTransaction = userTransaction;
21+
}
22+
23+
@Transactional
24+
public void executeTransfer(String fromAccontId, String toAccountId, BigDecimal amount) {
25+
bankAccountService.transfer(fromAccontId, toAccountId, amount);
26+
auditService.log(fromAccontId, toAccountId, amount);
27+
BigDecimal balance = bankAccountService.balanceOf(fromAccontId);
28+
if (balance.compareTo(BigDecimal.ZERO) <= 0) {
29+
throw new RuntimeException("Insufficient fund.");
30+
}
31+
}
32+
33+
public void executeTransferProgrammaticTx(String fromAccontId, String toAccountId, BigDecimal amount) throws Exception {
34+
userTransaction.begin();
35+
bankAccountService.transfer(fromAccontId, toAccountId, amount);
36+
auditService.log(fromAccontId, toAccountId, amount);
37+
BigDecimal balance = bankAccountService.balanceOf(fromAccontId);
38+
if (balance.compareTo(BigDecimal.ZERO) <= 0) {
39+
userTransaction.rollback();
40+
throw new RuntimeException("Insufficient fund.");
41+
} else {
42+
userTransaction.commit();
43+
}
44+
}
45+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.baeldung.jtademo.services;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.beans.factory.annotation.Qualifier;
5+
import org.springframework.core.io.DefaultResourceLoader;
6+
import org.springframework.core.io.Resource;
7+
import org.springframework.jdbc.core.JdbcTemplate;
8+
import org.springframework.jdbc.datasource.init.ScriptUtils;
9+
import org.springframework.stereotype.Component;
10+
11+
import javax.sql.DataSource;
12+
import java.sql.Connection;
13+
import java.sql.SQLException;
14+
15+
@Component
16+
public class TestHelper {
17+
final JdbcTemplate jdbcTemplateAccount;
18+
19+
final JdbcTemplate jdbcTemplateAudit;
20+
21+
@Autowired
22+
public TestHelper(@Qualifier("jdbcTemplateAccount") JdbcTemplate jdbcTemplateAccount, @Qualifier("jdbcTemplateAudit") JdbcTemplate jdbcTemplateAudit) {
23+
this.jdbcTemplateAccount = jdbcTemplateAccount;
24+
this.jdbcTemplateAudit = jdbcTemplateAudit;
25+
}
26+
27+
public void runAccountDbInit() throws SQLException {
28+
runScript("account.sql", jdbcTemplateAccount.getDataSource());
29+
}
30+
31+
public void runAuditDbInit() throws SQLException {
32+
runScript("audit.sql", jdbcTemplateAudit.getDataSource());
33+
}
34+
35+
private void runScript(String scriptName, DataSource dataSouorce) throws SQLException {
36+
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
37+
Resource script = resourceLoader.getResource(scriptName);
38+
try (Connection con = dataSouorce.getConnection()) {
39+
ScriptUtils.executeSqlScript(con, script);
40+
}
41+
}
42+
43+
}

jta/src/main/resources/account.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DROP SCHEMA PUBLIC CASCADE;
2+
3+
create table ACCOUNT (
4+
ID char(8) PRIMARY KEY,
5+
BALANCE NUMERIC(28,10)
6+
);
7+
8+
insert into ACCOUNT(ID, BALANCE) values ('a0000001', 1000);
9+
insert into ACCOUNT(ID, BALANCE) values ('a0000002', 2000);

jta/src/main/resources/application.properties

Whitespace-only changes.

jta/src/main/resources/audit.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
DROP SCHEMA PUBLIC CASCADE;
2+
3+
create table AUDIT_LOG (
4+
ID INTEGER IDENTITY PRIMARY KEY,
5+
FROM_ACCOUNT varchar(8),
6+
TO_ACCOUNT varchar(8),
7+
AMOUNT numeric(28,10)
8+
);

0 commit comments

Comments
 (0)