Skip to main content

JOTM Transactions In Spring And Hibernate

August 31, 2006

{cs.r.title}






Any enterprise-class application that interacts with databases needs to take care of transactional integrity across database
tables, databases, and components. Typically you achieve this either by using the heavy plumbing of application servers or by leveraging
the Java Transaction API (JTA) at code level. Both approaches have their merits and
demerits.

In the case of application servers, someone has to pay for all the infrastructure services, but the server platform does provide an
out-of-the-box solution for cross-cutting concerns like security, transaction, and persistence. Server infrastructures like those
provided by BEA or IBM address the full process associated with architecture, prototype, design, development, packaging, deployment, and
monitoring.

If instead you go for direct JTA API integration at the application code level, you don't need to pay for the additional server
infrastructure, but you may end up with business logic mixed with infrastructure-level code.

It is in this context that you need to think of lightweight containers that will help you wire application logic components with
infrastructure services like transaction handling without the necessity of having a full-blown application server. href="http://www.springframework.org/">Spring is one such lightweight container that uses Aspect Oriented Programming (AOP)
techniques for integrating cross-cutting services with components. This article walks you through a running example implemented
using a mix of three powerful, complementary J2EE technology frameworks: Spring, href="http://www.hibernate.org/">Hibernate, and JOTM.

The Transaction Problem In the Component World

This article explains the problem to be solved here with the help of a real-world example: the process of booking a trip.
The process involves at least three activities:

  1. Reserving a flight
  2. Booking a hotel
  3. Booking a car

The example scenario is illustrated in Figure 1.

Booking A Trip
Figure 1. Booking a trip

The problem gets tough if the above three operations must be done in different databases and these are provided by multiple
vendors. The scenario is complicated further if you need to coordinate the transactions across components, which again maps to
multiple vendor databases. High-end application servers address this problem of "transactional integrity across components" with the
help of "Transactional Objects," such as Java EE's Enterprise JavaBeans (EJB), href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwritingservicedcomponents.asp">.NET's Serviced
Components, and OMG's CORBA component with Object Transaction Service
(OTS)
. But all these transactional objects require a complex runtime environment to manage and coordinate their
transaction contexts. Naturally, this begs the question, "Why would one want to coordinate transactions at the component level, rather than
at the multiple database level in a single piece of code? The answer is multi-fold: modularity, manageability, configurability, and
last, but not least, reusability.

Components should contain business logic, not infrastructure logic; transaction control is a cross-cutting concern that clearly is
infrastructure logic. If the components are free of any infrastructure logic but contain business logic only, it is a huge advantage
because you can execute the same component in multiple runtime contexts, including multiple transaction contexts. For example, in the
sample scenario, the Car Rental Component can be executed in isolation just to make a car reservation. The same component can be
executed in a larger transaction like an entire travel reservation transaction that involves coordination with other components in
the transaction. Here this means using the same component to cater to different functional requirements.

Why Spring and JOTM Together?

The transaction capability discussed before is currently being fulfilled in the J2EE world by EJB components. Similar
capability is now available in Spring, which helps to weave transaction context to the runtime execution model. Let's now look at
how everything works in Spring.

The book Professional Java Development with the
Spring Framework
describes the situation like this:

Spring's transaction management capabilities—and especially its declarative transaction management—significantly
change traditional thinking as to when a J2EE application requires a full application server rather than a simple servlet engine. It's
not necessary to use an application server just to have declarative transactions via EJB

Figure 2 depicts how you integrate the different pieces. Spring is a non-invasive framework when it comes to wiring components
and propagating transaction context. This means you don't need to put anything specific at code level to attain transaction
management, especially anything specific to the runtime environment. Instead, you specify those details at the configuration level, or
by using attributes.

Components In Spring Context
Figure 2. Components In Spring context

Spring uses Dependency Injection to assemble component relationships. Component relationships are externalized in configuration files, and the
Spring runtime instantiates the required objects and sets relationships. Similarly, the transaction attributes are configured on a
per-instance level or on a per-method basis. If you combine these two capabilities, you gain a lot of advantages in the component world. The
important part is that the components configured in this way are not tied to any specific application server or runtime mechanisms.

Equally noteworthy is the fact that Spring components can be designed as Plain Old Java Objects (POJO) too. This means there is nothing to
prevent one from executing this Spring component dependency from within a high-end application server, if he or she so chooses.

Furthermore, Spring components follow the Interface base programming model. This means the client code will not change in case you need to swap
the implementation to a different runtime like EJB or CORBA.

The application block in Figure 2 depicts how you assemble various components of the travel service sample scenario in the Spring framework.
Various components in the sample scenario require persistence services, and you will use Hibernate as the Object Relationship (OR) mapping tool to
provide out-of-the-box persistence services.

Hibernate is going to talk to multiple resources since you are going to use multiple databases. Each such resource will have its own Resource
Manager. Since you want to deal with multiple resources in the sample, you have to use Java Transaction
API (JTA)
services from the application block. JTA provides the application-level interface to coordinate a transaction across multiple
resources. If this has to happen, the various Resource Managers have to interoperate, and this is achieved by all Resource Managers adhering to
the X/Open XA Resource Manager interface.

Here you need multiple connectors to coordinate in the same transaction, and you need a JTA-compliant Transaction Manager. It is here you are
going to use JOTM. Such Transaction Managers are also called Transaction Processing (TP) Monitors; Tuxedo from BEA and CICS from IBM are TP
monitors that guarantee high reliability and quality of service.

Java Open Transaction Manager (JOTM) is a JTA-compliant Transaction Manager. In fact, JOTM is a set of related classes, as shown in Figure
3.

JOTM Classes In Spring
Figure 3. JOTM classes In Spring

Using JOTM, you are able to retrieve both href="http://java.sun.com/javaee/5/docs/api/javax/transaction/TransactionManager.html">TransactionManager and href="http://java.sun.com/javaee/5/docs/api/javax/transaction/UserTransaction.html">UserTransaction objects. When dealing with an application
server with JTA support or when dealing with JTA-compliant transaction managers like JOTM, you will use Spring's href="http://www.springframework.org/docs/api/org/springframework/transaction/jta/class-use/JtaTransactionManager.html">org.springframework.
transaction.jta.JtaTransactionManager, which is the PlatformTransactionManager implementation in Spring. This means you
can still integrate Transaction Manager implementations that are available in high-end application servers like BEA's WebLogic Server. In the case
of WebLogic, you can use Spring's WebLogicJtaTransactionManager to delegate transaction calls to JTA, enabling transaction control
across components, applications, domains, clusters, and nodes.

This example uses Spring's JtaTransactionManager and JOTM, which will be wired together in the
applicationContext.xml, as shown in Listing 1. One main point to be noted is that JOTM is not a Resource Manager but a Transaction
Manager. So JOTM does not handle JDBC, JMS, or JCA resources, but handles XA Resources. In other words, JOTM will delegate calls to underlying
Resource Managers to complete the two-phase commit protocol. Irrespective of
the nature of the Resource Manager (whether it is a database or a message-oriented middleware or a Java Connector Resource), JOTM can be used to
coordinate transactions.

<beans>
<bean id="jotm" class="org.springframework
  .transaction.jta.JotmFactoryBean"/>
<bean id="transactionManager1" class="org.
   springframework.transaction.jta.
   JtaTransactionManager">
  <property name="userTransaction">
   <ref local="jotm"/>
  </property>
</bean>
<bean id="flightManager" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="flightManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveFlight">
     PROPAGATION_REQUIRED
    </prop>
   </props>
  </property>
</bean>
<beans>

Listing 1. Configuring JOTM in Spring

Integrating the Sample Application

The next step is to integrate the sample application components with JOTM and Spring Transactional components, as shown in Figure 2. This is
done in the applicationContext.xml, as shown in Listing 1. Assembling components in this way is the major advantage of using Spring.
As promised by the EJB specification and here in Spring as well, the application development and application assembling processes are decoupled.
Moreover, this aspect is taken out of application code and is now externalized to configuration files. This is real power that every application
developer in every technology would like to have, and you have that in Java EE now with the help of application frameworks.

Figure 4 shows the components required for the sample in the business services and in the Data Access Object layer. The structure shown in the
diagram is obtained by configuring component relationships in the configuration file.

Sample Business Services / DAO Layer
Figure 4. Sample business services/DAO layer

Each of the individual classes in the composite structure follows the interface-driven implementation: the TravelBrokerImpl is a
facade that composes the individual components and exposes the reservation service. FlightManagerImpl, HotelManagerImpl,
and CarManagerImpl are the three business component services. These services will do respective reservations and, if the resource is
not available, will throw specific exceptions of type TravelException. A little bit of tweaking is done in the code to
demonstrate various transactional behaviors. The business component services delegates all persistence calls to the Hibernate Data Access Object (DAO)
layer. Listings 2 through 5 provide the code for better understanding of how to manage to produce exception scenarios for demonstration.

public class TravelBrokerImpl
  implements TravelBroker{

public Trip bookTrip(BookingRequest
   bookingRequest) throws TravelException{

  Flight flight = flightManager.
   reserveFlight(bookingRequest);
  Hotel hotel = hotelManager.
   reserveHotel(bookingRequest);
  Car car = carManager.
   reserveCar(bookingRequest);

  if( (null == flight) ||
      (null == hotel)  ||
      (null == car)){
   throw new TravelCompletionException();
  }
  return new Trip(flight, hotel, car);
}
}

Listing 2. TravelBrokerImpl.java

public class FlightManagerImpl
  implements FlightManager{

public Flight reserveFlight
   (BookingRequest bookingRequest)
   throws FlightNotFoundException{

  Flight flight =
    getAvailableFlight(bookingRequest);
  if (flight == null){
   throw new FlightNotFoundException
    ("Flight not available");
  }
  flight.setBookings(new Integer(1));
  flightDao.reserveFlight(flight);
  return flight;
}
}

Listing 3. FlightManagerImpl.java

public class HotelManagerImpl
  implements HotelManager{

public Hotel reserveHotel
   (BookingRequest bookingRequest)
   throws HotelNotFoundException{

  Hotel hotel =
   getAvailableHotel(bookingRequest);
  // Exception not throwing below
  // for demonstration purposes
  /*
  if (hotel == null){
   throw new HotelNotFoundException
    ("Hotel not available");
  }
  */
  if (hotel == null){
   System.out.println("HotelManagerImpl:
    reserveHotel()-Hotel not available");
  }
  if(null != hotel){
   hotel.setBookings(new Integer(1));
   hotelDao.reserveHotel(hotel);
  }
  return hotel;
}
}

Listing 4. HotelManagerImpl.java

public class CarManagerImpl
  implements CarManager{

public Car reserveCar
   (BookingRequest bookingRequest)
   throws CarNotFoundException{

  //Get a car for Requested Date
  Car car = getExactCar(bookingRequest);
  if (car == null){
   //Get a car for near Date
   car = getAvailableCar(bookingRequest);
  }
  carDao.reserveCar(car);
  Calendar checkDate =
   Calendar.getInstance();
  checkDate.setTime(
   bookingRequest.getTraveldate());
  checkDate.add(Calendar.DAY_OF_MONTH,1);
  if (car.getBookingdate().
    compareTo(checkDate.getTime())>0)
   throw new CarNotFoundException(
    "Car date does not match.");
   return car;
  }
}
}

Listing 5. CarManagerImpl.java

Since you are following interface-based programming, swapping a component with another implementation is just a matter of changing the
relationship in the configuration. Also, in line with the discussion earlier, neither the facade nor the business components have a single line of
code to control transaction, but you are going to see how you can take control of the transactional scenarios to the maximum level required by the
application. The DAO layer classes manage the persistence-related operations to be done for the Business Domain Object Model (BDOM) classes shown
in Figure 5 using Hibernate.

Business Domain Object Model
Figure 5. Business Domain Object Model (BDOM)

You also need a few exception classes, as shown in Figure 6. These exception classes will play a critical role in controlling the
behavior of transactions based on what you have configured in the Spring. This feature is not specified in EJB specification but still is going to
give you a lot of flexibility in controlling transactions as is evident from the sample application, which will be described shortly.

Exception Class Design
Figure 6. Exception class design

Transaction Attributes: Spring vs. EJB

Both Spring and EJB give the user the freedom to choose from programmatic and declarative transaction management. For programmatic transaction
management, you need to code against the JDBC and JTA APIs. The disadvantages in using programmatic transaction management has already been covered here,
therefore let's explore the alternative: declarative transaction management. With the declarative approach, you externalize transaction control to
configuration files. Also, you need to choose from the available transaction attributes to get the required behavior. The EJB specification has already
defined six basic transaction attributes. Similarly, Spring has counterparts for all six different transaction attributes. In fact, Spring has more, as
listed below.

  1. PROPAGATION_REQUIRED (REQUIRED in EJB): Support a current transaction, create a new one if none exists.
  2. PROPAGATION_REQUIRES_NEW (REQUIRES_NEW in EJB): Create a new transaction, suspend the current transaction if one exists.
  3. PROPAGATION_NOT_SUPPORTED (NOT_SUPPORTED in EJB): Execute non-transactionally, suspend the current transaction if one exists.
  4. PROPAGATION_SUPPORTS (SUPPORTS in EJB): Support a current transaction, execute non-transactionally if none exists.
  5. PROPAGATION_MANDATORY (MANDATORY in EJB): Support a current transaction, throw an exception if none exists.
  6. PROPAGATION_NEVER (NEVER in EJB): Execute non-transactionally, throw an exception if a transaction exists.
  7. PROPAGATION_NESTED (No equivalent in EJB): Execute within a nested transaction if a current transaction exists, or else behave like
    PROPAGATION_REQUIRED. As of this writing, Spring's support for nested transactions is limited to single resource transaction managers
    supporting the JDBC 3.0 Save Point feature.

Demonstrating Transaction Control in a Sample Application

Having developed the business components, now it's time to concentrate on how to integrate them with the runtime context to get the required runtime
behavior. The interesting part is that you are now going to mix and match the transaction attributes to create completely different runtime behavior for
your components. Going forward, code will not be discussed in detail but various configuration details will be covered. Listings 6 and
Listing 7 show two different configurations for the same set of business components.

In Listing 6, the components are configured for an "All or Nothing Commit/Rollback" scenario. This means a single coarse-grained business
transaction may involve the call stack to propagate through multiple business component methods, and the end state of this coarse-grained transaction is
dependent on the cumulative success or failure of all the involved (or composed) transactions. This is achieved by giving the transaction attribute
PROPAGATION_REQUIRED for all the component methods. The end result is either "All" or "Nothing." To control the failure scenarios, Spring gives you the
flexibility to make decisions based on the type of exception thrown. This is achieved by specifying the type of exception thrown as another transaction
control-level attribute. Prefixing a "-" sign specifies to roll back the TX, and a "+" sign specifies to commit the TX.

<beans>
<bean id="flightManager" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="flightManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveFlight">
     PROPAGATION_REQUIRED
    </prop>
   </props>
  </property>
</bean>
<bean id="hotelManager" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="hotelManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveHotel">
     PROPAGATION_REQUIRED
    </prop>
   </props>
  </property>
  </bean>
<bean id="<font color=
"blue">carManagerRequired</font>" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="carManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveCar">
     <font color="blue">PROPAGATION_REQUIRED</font>,
     -com.example.exception.
     CarNotFoundException
    </prop>
   </props>
  </property>
</bean>
<bean id="<font color="blue">travelBrokerTargetRequired</font>"
   class="com.example.service.impl.
   TravelBrokerImpl">
  <property name="flightManager">
   <ref local="flightManager"/>
  </property>
  <property name="hotelManager">
   <ref local="hotelManager"/>
  </property>
  <property name="carManager">
   <ref local="<font color=
"blue">carManagerRequired</font>"/>
  </property>
</bean>
<bean id="<font color=
"blue">travelBrokerRequired</font>" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager2"/>
  </property>
  <property name="target">
   <ref local="<font color=
"blue">travelBrokerTargetRequired</font>"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="bookTrip">
     PROPAGATION_REQUIRED,
     <font color="blue">-com.example.exception.
     TravelException</font>
    </prop>
   </props>
  </property>
</bean>
</beans>

Listing 6. All or Nothing Commit-Rollback configuration

<beans>
<bean id="flightManager" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="flightManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveFlight">
     PROPAGATION_REQUIRED
    </prop>
   </props>
  </property>
</bean>
<bean id="hotelManager" class="org.
   springframework.transaction.interceptor.
   TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="hotelManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveHotel">
     PROPAGATION_REQUIRED
    </prop>
   </props>
  </property>
</bean>
<bean id="<font color=
"blue">carManagerRequiresNew</font>" class=
   "org.springframework.transaction.
   interceptor.TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager1"/>
  </property>
  <property name="target">
   <ref local="carManagerTarget"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="reserveCar">
     <font color="blue">PROPAGATION_REQUIRES_NEW</font>,
      -com.example.exception.
      CarNotFoundException
    </prop>
   </props>
  </property>
</bean>
<bean id="<font color=
"blue">travelBrokerTargetRequiresNew</font>"
   class="com.example.service.impl.
   TravelBrokerImpl">
  <property name="flightManager">
   <ref local="flightManager"/>
  </property>
  <property name="hotelManager">
   <ref local="hotelManager"/>
  </property>
  <property name="carManager">
   <ref local="<font color=
"blue">carManagerRequiresNew</font>"/>
  </property>
</bean>
<bean id="<font color=
"blue">travelBrokerRequiresNew</font>" class=
   "org.springframework.transaction.
   interceptor.TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager2"/>
  </property>
  <property name="target">
   <ref local="<font color=
"blue">travelBrokerTargetRequiresNew</font>"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="bookTrip">
     PROPAGATION_REQUIRED,
     <font color="blue">-com.example.exception.
     TravelCompletionException</font>
    </prop>
   </props>
  </property>
</bean>
</beans>

Listing 7. All or Some Commit-Rollback configuration

The above concepts are demonstrated in the sample application by executing the following commands:

ant run1
ant run2
run1 results in a successful transaction, as shown in Figure 7. But during run2, one of the composed transactions (booking a
car) fails, and this failed transaction will result in rolling back the entire transaction so that all the three database tables will be empty.

Figure 7
Figure 7. All Commit Scenario (click for a full-sized image)

The next step is the most important part of this whole article; it demonstrates how a different component assembly can show a different
runtime behavior, without any change in the code. For this, you will have a slightly different configuration, as shown in Listing 7. Line 48 (colored) is
where you deploy the same business component with a different transaction attribute. You give the transaction attribute of
PROPAGATION_REQUIRES_NEW instead of PROPAGATION_REQUIRED for the car booking component. A PROPAGATION_REQUIRES_NEW
will create a new transaction by suspending the current transaction if one exists. This means the success or failure condition of this transaction will
not have an impact on the parent transaction (if any).

Running the command:

ant run3

demonstrates this condition by throwing an exception for the car booking transaction. This will result in rollback of the car booking transaction, but
the flight booking and hotel booking transactions in the same flow will succeed, as shown in Figure 8.

Figure 8
Figure 8. Partial success scenario (click for a full-sized image)

I Am Flat!

The next most important part of this discussion is all about flat transactions, not nested
transactions
. The two have subtle differences.

"flat" transactions means a single atomic unit of work, internally split up into multiple steps. A failure in any of the steps will roll back the
entire transaction. This is the de facto standard for database operations, and the EJB specification also deals with flat transactions only. Flat
transactions are necessary and sufficient for transactions like fund transfers.

But for a different scenario like booking a trip, which involves multiple sectors and availability checks across multiple airline systems, there are
scenarios where we need nested transactions. With nested transactions, there is a root transaction with atomic units or sub-transactions embedded into it.
If any of the sub-transactions fail, it will not roll back the parent transaction, but the parent transaction can decide on the fate of each and every
sub-transaction as well as its own fate. An example scenario is when we book the flight, after reserving with the first sector airline we find that the
seat preference is not available with the airline we chose for the second sector. At this point we might want to roll back our transaction with the second
sector airline without losing the previous sub-transaction, and then try with an alternate airline or route.

The component assembly for this sample scenario with PROPAGATION_REQUIRES_NEW configuration is not emulating a nested transaction. This is
verified with run4, where you roll back the parent transaction. Here, all sub-transactions that are in the same parent transaction (configured
with PROPAGATION_REQUIRED) will be rolled back. Still, the sub-transaction with a PROPAGATION_REQUIRES_NEW is committed, which
ends up inserting data into the car reservation table. This is different from the nested transaction behavior described previously. Last but not
least, Spring does support nested transactions, but with a single connector only. For this, the driver should support JDBC 3.0's save point feature.

Conclusion

This article explored how three Java technologies—Spring, Hibernate, and JOTM—can be combined to effectively control transactions across
multi-vendor databases. There are a few other commercially available frameworks like JOTM, but JOTM is open-source and freely available. Using JOTM, you
can control flat transactions across components and across multiple databases. But there are other transaction models like Nested, Chained, and Sagas,
which haven't been discussed in detail in this article. Similarly, for transactions across Web services, you also need non-trivial ways to control and
coordinate. Business Transaction Protocol (BTP) is an
emerging technology that helps to manage transactions in loosely coupled, asynchronous environments where traditional techniques of resource locking no
longer work. Going forward, readers are advised to explore the BTP implementation of JOTM.

Resources


width="1" height="1" border="0" alt=" " />
Binildas Christudas currently works as a Principal Architect for Infosys Technologies, where he heads the J2EE Architects group servicing Communications Service Provider clients.