diff --git a/jta/pom.xml b/jta/pom.xml
new file mode 100644
index 0000000000..3eb0e294d5
--- /dev/null
+++ b/jta/pom.xml
@@ -0,0 +1,60 @@
+
+
+	4.0.0
+	com.baeldung
+	jta-demo
+	1.0-SNAPSHOT
+	jar
+
+	JEE JTA demo
+
+	
+		org.springframework.boot
+		spring-boot-starter-parent
+		2.0.4.RELEASE
+		 
+	
+
+	
+		UTF-8
+		UTF-8
+		1.8
+	
+
+	
+		
+			org.springframework.boot
+			spring-boot-starter-jta-bitronix
+		
+        
+            org.springframework.boot
+            spring-boot-starter-jdbc
+        
+		
+			org.springframework.boot
+			spring-boot-starter
+		
+
+		
+			org.springframework.boot
+			spring-boot-starter-test
+			test
+		
+
+        
+            org.hsqldb
+            hsqldb
+            2.4.1
+        
+    
+
+	
+		
+			
+				org.springframework.boot
+				spring-boot-maven-plugin
+			
+		
+	
+
diff --git a/jta/src/main/java/com/baeldung/jtademo/JtaDemoApplication.java b/jta/src/main/java/com/baeldung/jtademo/JtaDemoApplication.java
new file mode 100644
index 0000000000..f7b1cbbb4d
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/JtaDemoApplication.java
@@ -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);
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/dto/TransferLog.java b/jta/src/main/java/com/baeldung/jtademo/dto/TransferLog.java
new file mode 100644
index 0000000000..cc1474ce81
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/dto/TransferLog.java
@@ -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;
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/services/AuditService.java b/jta/src/main/java/com/baeldung/jtademo/services/AuditService.java
new file mode 100644
index 0000000000..098a6f9e1f
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/services/AuditService.java
@@ -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);
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/services/BankAccountManualTxService.java b/jta/src/main/java/com/baeldung/jtademo/services/BankAccountManualTxService.java
new file mode 100644
index 0000000000..acbef33427
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/services/BankAccountManualTxService.java
@@ -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();
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/services/BankAccountService.java b/jta/src/main/java/com/baeldung/jtademo/services/BankAccountService.java
new file mode 100644
index 0000000000..bb59437add
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/services/BankAccountService.java
@@ -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);
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/services/TellerService.java b/jta/src/main/java/com/baeldung/jtademo/services/TellerService.java
new file mode 100644
index 0000000000..8bef892ca2
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/services/TellerService.java
@@ -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!");
+    }
+}
diff --git a/jta/src/main/java/com/baeldung/jtademo/services/TestHelper.java b/jta/src/main/java/com/baeldung/jtademo/services/TestHelper.java
new file mode 100644
index 0000000000..21370cf034
--- /dev/null
+++ b/jta/src/main/java/com/baeldung/jtademo/services/TestHelper.java
@@ -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) (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) (rs) -> {
+            rs.next();
+            return new BigDecimal(rs.getDouble(1));
+        });
+    }
+}
diff --git a/jta/src/main/resources/account.sql b/jta/src/main/resources/account.sql
new file mode 100644
index 0000000000..af14f89b01
--- /dev/null
+++ b/jta/src/main/resources/account.sql
@@ -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);
\ No newline at end of file
diff --git a/jta/src/main/resources/application.properties b/jta/src/main/resources/application.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/jta/src/main/resources/audit.sql b/jta/src/main/resources/audit.sql
new file mode 100644
index 0000000000..aa5845f402
--- /dev/null
+++ b/jta/src/main/resources/audit.sql
@@ -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)
+);
\ No newline at end of file
diff --git a/jta/src/test/java/com/baeldung/jtademo/JtaDemoUnitTest.java b/jta/src/test/java/com/baeldung/jtademo/JtaDemoUnitTest.java
new file mode 100644
index 0000000000..68ce5531a1
--- /dev/null
+++ b/jta/src/test/java/com/baeldung/jtademo/JtaDemoUnitTest.java
@@ -0,0 +1,70 @@
+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 org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.math.BigDecimal;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = JtaDemoApplication.class)
+public class JtaDemoUnitTest {
+    @Autowired
+    TestHelper testHelper;
+
+    @Autowired
+    TellerService tellerService;
+
+    @Autowired
+    BankAccountManualTxService bankAccountManualTxService;
+
+    @Before
+    public void beforeTest() throws Exception {
+        testHelper.runAuditDbInit();
+        testHelper.runAccountDbInit();
+    }
+
+    @Test
+    public void 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));
+
+        TransferLog lastTransferLog = testHelper.lastTransferLog();
+        assertThat(lastTransferLog).isNotNull();
+        assertThat(lastTransferLog.getFromAccountId()).isEqualTo("a0000001");
+        assertThat(lastTransferLog.getToAccountId()).isEqualTo("a0000002");
+        assertThat(lastTransferLog.getAmount()).isEqualByComparingTo(BigDecimal.valueOf(500));
+    }
+
+    @Test
+    public void whenException_thenAllRolledBack() throws Exception {
+        assertThatThrownBy(() -> {
+            tellerService.executeTransferFail("a0000002", "a0000001", BigDecimal.valueOf(100));
+        }).hasMessage("Something wrong, rollback!");
+
+        assertThat(testHelper.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(1000));
+        assertThat(testHelper.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2000));
+
+        assertThat(testHelper.lastTransferLog()).isNull();
+    }
+
+    @Test
+    public void givenBMT_whenNoException_thenAllCommitted() throws Exception {
+        bankAccountManualTxService.transfer("a0000001", "a0000002", BigDecimal.valueOf(100));
+
+        assertThat(testHelper.balanceOf("a0000001")).isEqualByComparingTo(BigDecimal.valueOf(900));
+        assertThat(testHelper.balanceOf("a0000002")).isEqualByComparingTo(BigDecimal.valueOf(2100));
+    }
+}
diff --git a/pom.xml b/pom.xml
index 7ff1aa7d47..b95a874537 100644
--- a/pom.xml
+++ b/pom.xml
@@ -593,6 +593,7 @@
 				spring-reactive-kotlin
 				jnosql
 				spring-boot-angular-ecommerce
+				jta