Spring Jta with Jboss

Have you ever tried to have to link, in unique transaction, two or more datasources from different databases? Java Transaction API (JTA) helps you to achieve this result.

In this article I’ll try to show how use it with spring framework and Jboss application server, obviously, with a little practical example.

As I was saying, I use a code about store market for illustrating the distributed transaction using different data sources. In that code I’ve the “Order” and the “Ware House” datasources.

The idea is to receive an order and then, once store in order database, update the ware house quantity of that product order.

As you can see, whether something in the order process goes wrong, the operation needs a completely rollback on the different datasources. We are talking about a classical ACID (atomicity, consistency, isolation, durability) method.

For this purpose I used a Spring framework integration for JTA (http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html) and Jboss 7 application server. Also, Hibernate was used for Jpa interface and HSqlDb as database resource.

Probably you need to install Jboss plugin in Eclipse Indigo version. If you need it, check out this url to do it (http://kousikraj.wordpress.com/tag/how-to-install-jboss-7-runtime-in-eclipse/)

After that, you can configure your hsqldb. It’s very easy to run, just lunch this from command line.

java -cp WebContent/WEB-INF/lib/hsqldb-2.2.4.jar org.hsqldb.server.Server --database.0 file:db/ordersdb --dbname.0 ordersdb --database.1 file:db/warehousedb --dbname.1 warehousedb

The result is to run two database instances: OrdersDb and WareHouseDB.

Check out the result running the console administration.

java -classpath WebContent/WEB-INF/lib/hsqldb-2.2.4.jar org.hsqldb.util.DatabaseManagerSwing -user sa -url jdbc:hsqldb:hsql://localhost/ordersdb

Next step is configuring JNDI resources inside Jboss application server. Very briefly connect to url http://localhost:9990 and then browse under Deployments->Manage Deployments and load the hsqldb-2.2.4.jar drivers.

Once load it browse under Profile->Connector->Datasources and register the two datasources java:/OrdersDS  and java:/WareHouseDS with the previous driver.

Now, some code. First is persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

	<persistence-unit name="Orders" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:/OrdersDS</jta-data-source>
		<class>it.samplejtaj2ee.bean.Order</class>
		<exclude-unlisted-classes>true</exclude-unlisted-classes>
		<properties>
			<property name="hibernate.transaction.manager_lookup_class"
				value="org.hibernate.transaction.JBossTransactionManagerLookup" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
			<property name="hibernate.hbm2ddl.auto" value="create" />
		</properties>
	</persistence-unit>

	<persistence-unit name="WareHouse" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:/WareHouseDS</jta-data-source>
		<class>it.samplejtaj2ee.bean.WareHouse</class>
		<exclude-unlisted-classes>true</exclude-unlisted-classes>

		<properties>
			<property name="hibernate.transaction.manager_lookup_class"
				value="org.hibernate.transaction.JBossTransactionManagerLookup" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
			<property name="hibernate.hbm2ddl.auto" value="create" />
		</properties>
	</persistence-unit>

</persistence>

and the entities class:

package it.samplejtaj2ee.bean;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "ORDERS")
public class Order {
 
 private Long id;
 private String product;
 private int quantity;
 
 @Id
 @GeneratedValue
 @Column(name = "ID")
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }
 
 @Column(name = "PRODUCT")
 public String getProduct() {
  return product;
 }
 public void setProduct(String product) {
  this.product = product;
 }
 
 @Column(name = "QUANTITY")
 public int getQuantity() {
  return quantity;
 }
 public void setQuantity(int quantity) {
  this.quantity = quantity;
 }
}
package it.samplejtaj2ee.bean;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.Table;

@Entity
@Table(name = "WAREHOUSE")
@NamedQueries({
   @javax.persistence.NamedQuery(
     name="emptyWareHouse",
     query="DELETE FROM WareHouse"
   )
 })

public class WareHouse {

 private String product;
 private int quantity;

 @Id
 @Column(name = "PRODUCT")
 public String getProduct() {
  return product;
 }

 public void setProduct(String product) {
  this.product = product;
 }

 @Column(name = "QUANTITY")
 public int getQuantity() {
  return quantity;
 }

 public void setQuantity(int quantity) {
  this.quantity = quantity;
 }
}

The spring integration through the configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
       	http://www.springframework.org/schema/aop/spring-aop.xsd
       	http://www.springframework.org/schema/tx
       	http://www.springframework.org/schema/tx/spring-tx.xsd
       	http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- JPAAccountDAO has JPA annotations to access EntityManager -->
	<context:annotation-config />

	<bean id="ordersEntityManager"
		class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="Orders" />
	</bean>

	<bean id="wareHouseEntityManager"
		class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="WareHouse" />
	</bean>

	<bean id="orderServlet" class="it.samplejtaj2ee.servlet.OrdersServlet" />

	<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />

	<bean id="orderViewJson" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />

	<bean id="ordersDao" class="it.samplejtaj2ee.dao.OrdersDao" />
	<bean id="wareHouseDao" class="it.samplejtaj2ee.dao.WareHouseDao" />

	<bean id="ordersController" class="it.samplejtaj2ee.controller.OrdersController"
		scope="singleton">
		<property name="ordersDao" ref="ordersDao" />
 		<property name="wareHouseDao" ref="wareHouseDao" />
	</bean>

	<!-- Configure Transaction Support - Access the JTA transaction manager -->
	<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>

Take a look from line 45. I configured a Jta transaction manager with Jboss resources through JNDI.

The Dao configuration is very easy for a both resources.

package it.samplejtaj2ee.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;

import it.samplejtaj2ee.bean.Order;

public class OrdersDao{

	@PersistenceContext(unitName="ordersEntityManager")
	private EntityManager em;

	@Transactional(readOnly = true)
	public void insertOrder(Order order) {
		em.persist(order);

	}
}
package it.samplejtaj2ee.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Transactional;

import it.samplejtaj2ee.bean.WareHouse;

public class WareHouseDao{

	@PersistenceContext(unitName="wareHouseEntityManager")
	private EntityManager em;

	@Transactional(readOnly = true)
	public WareHouse getOrderWareHouse(String product) {
		return em.find(WareHouse.class, product);
	}

	@Transactional(readOnly = true)
	public void emptyWareHouse()
	{
		Query query = em.createNamedQuery("emptyWareHouse");
		query.executeUpdate();
	}

	@Transactional(readOnly = true)
	public void populateWareHouse(WareHouse wareHouse) {
		em.persist(wareHouse);

	}

	public void updateWareHouse(WareHouse wareHouse) {
		em.merge(wareHouse);
	}
}

In the Dao controller class I put the transaction process. If something fails everything rollback.

package it.samplejtaj2ee.controller;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import it.samplejtaj2ee.bean.Order;
import it.samplejtaj2ee.bean.WareHouse;
import it.samplejtaj2ee.dao.OrdersDao;
import it.samplejtaj2ee.dao.WareHouseDao;

public class OrdersController {

 private OrdersDao ordersDao;
 private WareHouseDao wareHouseDao;
 private final boolean getError = true;
 
 public OrdersDao getOrdersDao() {
  return ordersDao;
 }
 public void setOrdersDao(OrdersDao ordersDao) {
  this.ordersDao = ordersDao;
 }
 public WareHouseDao getWareHouseDao() {
  return wareHouseDao;
 }
 public void setWareHouseDao(WareHouseDao wareHouseDao) {
  this.wareHouseDao = wareHouseDao;
 }
 
 public void emptyWareHouse()
 {
  wareHouseDao.emptyWareHouse();
 }
 
 @Transactional (propagation=Propagation.REQUIRED,
   rollbackFor=Exception.class)
 public void populateWareHouse()
 {
  WareHouse whArticle1 = new WareHouse();
  whArticle1.setProduct("CZ-123");
  whArticle1.setQuantity(5);
  
  wareHouseDao.populateWareHouse(whArticle1);
  
  WareHouse whArticle2 = new WareHouse();
  whArticle2.setProduct("AH-987");
  whArticle2.setQuantity(10);
  
  wareHouseDao.populateWareHouse(whArticle2);
 }
 
 @Transactional (propagation=Propagation.REQUIRED,
   rollbackFor=Exception.class)
 public Order purchaseProduct(Order order) throws Exception
 {  
  //Insert Order
  ordersDao.insertOrder(order);
 
  //Read the product from WareHouse
  WareHouse wareHouseProduct = wareHouseDao.getOrderWareHouse(order.getProduct());
  
  //Remove the quantity from WareHouse
  wareHouseProduct.setQuantity(wareHouseProduct.getQuantity()-order.getQuantity());
  
  //Update Warehouse
  wareHouseDao.updateWareHouse(wareHouseProduct);
  
  if (getError)
   throw new Exception("WareHouse Exception!!");
  else
   return order;
  
 }
}

You can switch the getError property for testing the rollback operation.

At last, the MVC Rest service that calls the business process.

package it.samplejtaj2ee.servlet;

import it.samplejtaj2ee.bean.Order;
import it.samplejtaj2ee.controller.OrdersController;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class OrdersServlet {

	@Autowired
	private OrdersController controller;

	@RequestMapping(method = RequestMethod.GET, value = "/json/orders")
	public ModelAndView getTrainsJson() {

		controller.emptyWareHouse();
		controller.populateWareHouse();

		Order bike = new Order();
		bike.setProduct("CZ-123");
		bike.setQuantity(1);

		Order bikeProcessed = null;
		try {
			bikeProcessed = controller.purchaseProduct(bike);
		} catch (Exception e) {
			bikeProcessed = new Order();
			bikeProcessed.setProduct(e.getMessage());
		}

		return new ModelAndView("orderViewJson", "order", bikeProcessed);
	}
}

Try it switching the getError property and check the different result. As you can see spring is very useful and good integrated with Jta technology.

The references to include in this project are the same used on http://blog.m1key.me/2011/05/jta-transactions-with-hibernate-jboss-6.html.

2 thoughts on “Spring Jta with Jboss”

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.