ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Wire Hibernate Transactions in Spring
Pages: 1, 2, 3

Running The Demo

For running the test cases, a JUnit test class, OrderListManagerTest, is provided within myusers\test\com\example\service. To execute it, run the following command from the command prompt where we built the application:

CATALINA_HOME\bin\ant test -Dtestcase=OrderListManager

The test case is divided into two main sections: the first section creates an order consisting of two line items, and then links the two line items to the order. This is shown below, and will run successfully:

OrderList orderList1 = new OrderList();
Long orderId1 = orderListManager.
        createOrderList(orderList1);
log("Created OrderList with id '"
        + orderId1 + "'...");
orderListManager.addLineItem(orderId1,lineItem1);
orderListManager.addLineItem(orderId1,lineItem2);

The next section performs a similar action, but this time we are trying to add three line items to the order, which will generate an exception:

OrderList orderList2 = new OrderList();
Long orderId2 = orderListManager.
        createOrderList(orderList2);
log("Created OrderList with id '"
        + orderId2 + "'...");
orderListManager.addLineItem(orderId2,lineItem3);
orderListManager.addLineItem(orderId2,lineItem4);
//We know, we will have an exception here,
        still want to proceed
try{
  orderListManager.addLineItem
        (orderId2,lineItem5);
}
catch(FacadeException facadeException){
  log("ERROR : " + facadeException.getMessage());
}

The console print out is shown in Figure 5:

Client Side Console Output
Figure 5. Client-side console output

We have created Order1, and added two line items with Line Item IDs 1 and 2 to it. Then we created Order2, and tried to add three items. Adding the first two line items (with Line Item IDs 3 and 4) was successful, but Figure 5 shows that when we tried to add a third item (with Line Item ID 5) to Order2, the business method met an exception. Hence, the business method TX is rolled back, and no Line Item with Line Item ID 5 is persisted in the database. This can be validated as shown in Figure 6 and Figure 7, which is possible by executing the following command from the console:

CATALINA_HOME\bin\ant browse1

Orders Created in appfuse1 DB
Figure 6. Orders created in the appfuse1 database

Line Items Created in appfuse1 DB
Figure 7. Line items created in the appfuse1 database

The next, and most important, part of the demonstration shows that orders and line items are persisted in appfuse1 database, whereas audit objects are persisted in appfuse2 database. In effect, the service methods in OrderListManager talk to multiple databases. Open up the appfuse2 database to see the audit trail, as shown below:

CATALINA_HOME\bin\ant browse2

Audit trail created in the appfuse2 database, with entry for failed TX
Figure 8. Audit trail created in the appfuse2 database, with entry for failed TX

The last row in Figure 8 needs special attention. The RESOURCE column says "this row corresponds to LineItem5." But if we go back to Figure 7, there is no line item corresponding to LineItem5. Has something gone wrong here? In fact, everything worked perfectly, and this extra row in Figure 7 is what this whole article has been about. We will now discuss what has happened here.

We know that addLineItem() method has PROPAGATION_REQUIRED and the log() method has PROPAGATION_REQUIRES_NEW. Further, addLineItem() internally calls the log() method. So when we tried to add a third line item to Order2, an exception is raised (as per our business rule), which will roll back both the creation of this line item and the linking of this line item with Order2. But, since a call to log() is also made from within addLineItem(), and since log() has the PROPAGATION_REQUIRES_NEW TX attribute, the rolling back of addLineItem() will not roll back log(), since log() happens in a new TX.

Let us now make a change in the TX attribute of log(). Instead of PROPAGATION_REQUIRES_NEW, change the TX attribute to PROPAGATION_SUPPORTS. The PROPAGATION_SUPPORTS attribute allows the service method to run in a client TX, if the client has a TX context, otherwise the method runs with no TX at all. You may have to reinstall the application so that the already-available data in the databases are automatically flushed. To reinstall, follow step 12 in the "Setting up the Demo Environment" section above.

If we repeat the run, we will experience a slightly different behavior this time. This time, we will still get an exception scenario when we try to add a third line item to Order 2. This will roll back the transaction that attempts to add the third line item. This method, in turn, calls the method log(). But since log() has a TX attribute of PROPAGATION_SUPPORTS, log() will be invoked in the same TX context as that of addLineItem() method. Since addLineItem() rolls back, the log() also rolls back, leaving no audit trace for the rolled-back TX. So there is no audit trail entry in Figure 9, corresponding to the failed TX!

Audit Trail Created In appfuse2 DB, Without Entry For Failed TX
Figure 9. Audit trail created in the appfuse2 database, without entry for failed TX

The only change we have done for this different transaction behavior is that we changed the TX attribute in the Spring configuration, as shown below:

<bean id="auditManager"
        class="org.springframework.transaction.
        interceptor.TransactionProxyFactoryBean">
    <property name="transactionAttributes">
        <props>
<!-- prop key="log">
                PROPAGATION_REQUIRES_NEW
            </prop -->
            <prop key="log">
                PROPAGATION_SUPPORTS
            </prop>
 
        </props>
    </property>
</bean>

This is the effect of declarative transaction management, which we have been leveraging since the inception of EJB. But we know that we need a high-end application server to host our EJB components. Now we have seen that similar results can be attained even without an EJB server, using Spring.

Summary

This article throws light onto one of the powerful combinations in the J2EE world: Spring and Hibernate. By leveraging the capabilities of both, we have alternate technology options for Container-Managed Persistence (CMP), Container-Managed Relationships (CMR), and Declarative Transaction Management. Even though Spring wasn't conceived as an alternative to EJB, it provides features, such as declarative transaction management for plain Java objects, that enable users to dispense with EJB in many projects.

It is not the aim of this article to find out an alternative to EJB, but we are trying to find out the best available technology options for the problems at hand. Hence, we need to explore further capabilities of the lightweight combination of Spring and Hibernate, and this is left as a subject for further exploration for the reader.

Resources

Binildas Christudas is a Senior Technical Architect at Communication Service Providers Practice (CSP) of Infosys, and is a Sun Microsystems Certified Enterprise Architect and a Microsoft Certified Professional.


Return to ONJava.com.