Using JBoss Transaction Manager in a Spring application

In this tutorial we will learn how to use JBoss Transaction Manager on the top of Spring’s transaction management abstractions.

The Spring framework allows pluggable transaction managers. However, iIf you need XA support for your Spring applications, then you need to plug in a transaction manager such as JBossTS.

Let’s see how to configure JBoss Transaction Manager in a sample application which executes Transaction Statements using Spring JDBCTemplate and the default JBoss/WildFly DataSource for H2 database.

Here is the simple Customer class which works as a model:

package com.sample;

public class Customer {

    private String name;
    private String surname;
    private int age;

    public Customer() {

    }

    public Customer(String name, String surname, int age) {
        this.name = name;
        this.surname = surname;
        this.age = age;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                ", age=" + age +
                '}';
    }
}

We will use a DAO interface to handle basic SQL Operations on the model:

package com.sample;
import java.util.List;


public interface JDBCCustomerDAO {

    public void insert(Customer customer);

    public List<Customer> findAll();

    public void createTable();
    public void dropTable();
}

Here is the DAO implementation, which contains a method tagger as @Transactional with the attribute REQUIRED:

package com.sample;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;


import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class JDBCCustomerDAOImpl implements JDBCCustomerDAO {
    private DataSource dataSource;
    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void createTable() {
        jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate
                .execute("create table customer (name varchar, surname varchar, age integer)");
        System.out.println("Table created!");
    }

    public void dropTable() {
        jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.execute("drop table customer");
        System.out.println("Table dropped!");
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void insert(Customer customer) {

        String sql = "INSERT INTO CUSTOMER "
                + "(NAME,SURNAME, AGE) VALUES (?, ?, ?)";

        jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.update(sql,
                new Object[] { customer.getName(), customer.getSurname(),
                        customer.getAge() });
    }

    @SuppressWarnings("rawtypes")
    public List<Customer> findAll() {

        jdbcTemplate = new JdbcTemplate(dataSource);
        String sql = "SELECT * FROM CUSTOMER";

        List<Customer> customers = new ArrayList<Customer>();

        List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
        for (Map row : rows) {
            Customer customer = new Customer();
            customer.setSurname((String) row.get("SURNAME"));
            customer.setName((String) row.get("NAME"));
            customer.setAge(Integer.parseInt(String.valueOf(row.get("AGE"))));
            customers.add(customer);
        }

        return customers;
    }

}

As soon as the Spring application is deployed, we will trigger the SQL Statements. For this purpose, we can deploy a WebListener which is fired when the application is deployed and undeployed:

package com.sample;


import javax.inject.Inject;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.List;

@WebListener
public class MyContextListener implements ServletContextListener {
    @Inject
    ConfigurableApplicationContext context;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        JDBCCustomerDAO dao = (JDBCCustomerDAO) context
                .getBean("jdbcCustomerDAO");

        dao.createTable();
        Customer c = new Customer();
        c.setAge(45);
        c.setName("ABC");
        c.setSurname("DEF");
        dao.insert(c);

        List<Customer> list = dao.findAll();
        for (Customer customer:list)
            System.out.println(customer);

    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        JDBCCustomerDAO dao = (JDBCCustomerDAO) context
                .getBean("jdbcCustomerDAO");

        dao.dropTable();

    }
}

In order to read Spring’s XML configuration, we can deploy an @ApplicationScoped CDI Bean which produces Spring’s ClassPathXmlApplicationContext from the applicationContext.xml:

package com.sample;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@ApplicationScoped
public class SpringProducer {
    @Produces
    public ConfigurableApplicationContext create() {
        return new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    public void close(@Disposes final ConfigurableApplicationContext ctx) {
        ctx.close();
    }
}

Lastly, we will add a RowMapper for our Customer Class:

package com.sample;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

@SuppressWarnings("rawtypes")
public class CustomerRowMapper implements RowMapper	{
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
        Customer customer = new Customer();
        customer.setSurname(rs.getString("SURNAME"));
        customer.setName(rs.getString("NAME"));
        customer.setAge(rs.getInt("AGE"));
        return customer;
    }
}

We are done with the code. Let’s add the applicationContext.xml file in the resources folder of your Maven project:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">

    <context:component-scan base-package="com.sample" />

    <bean id="jdbcCustomerDAO" class="com.sample.JDBCCustomerDAOImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/ExampleDS"/>

    <bean id="txManager"
          class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManagerName" value="java:jboss/TransactionManager" />
        <property name="userTransactionName" value="java:jboss/UserTransaction" />
    </bean>

    <tx:annotation-driven transaction-manager="txManager" />
    <tx:jta-transaction-manager />

</beans>

Please notice we are using as TransactionManager the JBoss TransactionManager which is looked up via JNDI. Also, we lookup the DataSource “java:jboss/datasources/ExampleDS” from the application server.

When we deploy the application, provided that we have set to TRACE the “com.arjuna.ats.jta”, we can see the Transaction enlist and delist of the transactional resource:

2020-09-30 14:25:46,563 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,564 DEBUG [org.springframework.jdbc.core.JdbcTemplate] (ServerService Thread Pool -- 92) Executing prepared SQL update
2020-09-30 14:25:46,564 DEBUG [org.springframework.jdbc.core.JdbcTemplate] (ServerService Thread Pool -- 92) Executing prepared SQL statement [INSERT INTO CUSTOMER (NAME,SURNAME, AGE) VALUES (?, ?, ?)]
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.enlistResource ( LocalXAResourceImpl@45d3cf25[connectionListener=2f98cca3 connectionManager=74240d7a warned=false currentXid=null productName=H2 productVersion=@PROJECT_VERSION@ (2016-10-31) jndiName=java:jboss/datasources/ExampleDS] )
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.arjuna] (ServerService Thread Pool -- 92) StateManager::StateManager( 0:0:0:0:1 )
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.arjuna] (ServerService Thread Pool -- 92) AbstractRecord::AbstractRecord (0:0:0:0:1)
2020-09-30 14:25:46,564 TRACE [com.arjuna.ats.arjuna] (ServerService Thread Pool -- 92) LastResourceRecord()
2020-09-30 14:25:46,565 TRACE [com.arjuna.ats.arjuna] (ServerService Thread Pool -- 92) RecordList::insert(RecordList: empty) : appending /StateManager/AbstractRecord/LastResourceRecord for 0:0:0:0:1
2020-09-30 14:25:46,567 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,568 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,569 DEBUG [org.springframework.jdbc.core.JdbcTemplate] (ServerService Thread Pool -- 92) SQL update affected 1 rows
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) BaseTransaction.commit
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.commitAndDisassociate
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) SynchronizationImple.beforeCompletion - Class: class org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization HashCode: 1256027315 toString: org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization@4add74b3
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) SynchronizationImple.beforeCompletion - Class: class org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider$1 HashCode: 706141901 toString: org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider$1@2a16decd
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) SynchronizationImple.beforeCompletion - Class: class org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization HashCode: 172725243 toString: org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization@a4b93fb
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.equals
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
2020-09-30 14:25:46,569 TRACE [com.arjuna.ats.jta] (ServerService Thread Pool -- 92) TransactionImple.delistResource ( LocalXAResourceImpl@45d3cf25[connectionListener=2f98cca3 connectionManager=74240d7a warned=false currentXid=< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a057e34:-7a596d84:5f745755:27d, node_name=1, branch_uid=0:ffff0a057e34:-7a596d84:5f745755:281, subordinatenodename=null, eis_name=java:jboss/datasources/ExampleDS > productName=H2 productVersion=@PROJECT_VERSION@ (2016-10-31) jndiName=java:jboss/datasources/ExampleDS], 67108864 )

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/spring/spring-demo-tx

Found the article helpful? if so please follow us on Socials