Welcome!

Open Source Authors: Liz McMillan, Maureen O'Gara, Jeremy Geelan, Reuven Cohen, Lavenya Dilip

Related Topics: Open Source, Java

Open Source: Article

Bringing Advanced Transaction Capabilities to Spring Applications

Inversion of control and dependency injection

Let's take a step-by-step look at our sample trading application showing how Spring can be used when an external transaction manager is required.

Let's use the Oracle Application Server transaction manager as an example that demonstrates the integration of JTA with Spring's OC4JJtaTransactionManager. The application demonstrates the classic distributed two-phase commit transaction use case requiring ACID properties: the bank account transfer. Funds are debited from one account and credited to another. Either both the debit and credit must occur or neither must occur. In this example, the transfer is from a bank account to a brokerage account to purchase individual stocks. The example includes a very simple MVC-style application consisting of a test controller, financial service, asset management service, and two data access objects representing a bank and a brokerage. Container-manager transactions are used. The example adds additional aspects to this scenario to demonstrate the extended features of the OC4JJtaTransactionManager that include named transactions and per-transaction isolation-level designation.

The following HowToJTASpringController implements the Spring Controller and InitializingBean interfaces. Note that the setFinancial method provides the FinancialService implementation (as specified in applicationContext.xml).

public class HowToJTASpringController implements InitializingBean,
Controller {
    private FinancialService m_financial;

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
       FinancialReport financialReport = m_financial.processFinancials();
       request.setAttribute("financialReport", financialReport);
       return new ModelAndView("/jsp/success.jsp");
    }
    catch (Exception e) {
       request.setAttribute("error", e.getMessage());
       return new ModelAndView("/jsp/error.jsp");
    }
    }

The FinancialServiceImpl class implements the Spring InitializingBean interface as well as the FinancialService interface. The setAssetManagement method is called by the Spring Framework, which also provides the AssetManagementService implementation (as specified in applicationContext.xml) using dependency injection. The Transactional class-level annotation (transaction annotation support is specified in applicationContext.xml) designates that business methods of this class, namely processFinancials, have a propagation value of REQUIRED. That is, the methods execute in a transaction if one exists or a transaction is started if none exists. The annotation also specifies that the transaction is to be readOnly and that the isolation level of any connections used in the transaction are set to SERIALIZABLE.

@Transactional(readOnly = true, propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)
public class FinancialServiceImpl implements InitializingBean, FinancialService {
       AssetManagementService m_assetManagementService;

    public FinancialReport processFinancials() {
       AssetReport assetReportBeforeStockPurchase = m_assetManagementService.
       reportAllAssets();
       StockPurchaseReport stockPurchaseReport = m_assetManagementService.
       purchaseNewStockAndReport();
       AssetReport assetReportAfterStockPurchase = m_assetManagementService.
       reportAllAssets();
       return new FinancialReport(assetReportBeforeStockPurchase, stockPur
       chaseReport, assetReportAfterStockPurchase);
    }

    public final void afterPropertiesSet() throws Exception {
       if (m_assetManagementService == null)
          throw new BeanCreationException("NoAssetManagementService was set. Verify context xml.");
    }

    public void setAssetManagement(AssetManagementService assetManagementService) {
          m_assetManagementService = assetManagementService;
    }
}

The AssetManagementServiceImpl class implements the Spring InitializingBean interface as well as the AssetManagementService interface. The setBank and setBrokerage methods are called by the Spring Framework providing the Bank and Brokerage DAO implementations (as specified in applicationContext.xml) using dependency injection.

The Transactional method-level annotation (transaction annotation support is specified in applicationContext.xml) designates that the purchaseNewStockAndReport method has a propagation value of REQUIRED. That is, it executes in a transaction if one exists, or a transaction is started if none exists. The annotation also specifies the isolation-level of any connections used in the transaction be set to READ_COMMITTED.

Another method-level Transactional annotation designates that the reportAllAssets method has a propagation value of SUPPORTS. That method executes in a transaction if one exists but doesn't throw an exception or start a transaction if none exists. The annotation also specifies that the noRollbackFor be set to ConcurrencyFailureException.class, which indicates that if a transaction exists and this Spring DAO RuntimeException is thrown, the transaction shouldn't rollback as a result.

public class AssetManagementServiceImpl implements InitializingBean, AssetManagementService {
    private Bank m_bank;
    private Brokerage m_brokerage;

    @Transactional(propagation = Propagation.SUPPORTS, noRollbackFor = ConcurrencyFailureException.class)
    public AssetReport reportAllAssets() {
       return new AssetReport(m_bank.selectBalance(), m_brokerage.selectAllStocks());
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public StockPurchaseReport purchaseNewStockAndReport() {
       int stockAmount = 10;
       String stockSymbol = "ABC";
       m_bank.updateBalance(m_bank.selectBalanceForUpdate() - stockAmount);
       m_brokerage.insertStock(stockSymbol, stockAmount);
       return new StockPurchaseReport(stockSymbol, stockAmount);
    }

    public final void afterPropertiesSet() throws Exception {
       if (m_bank == null) throw new BeanCreationException("No Bank was set. Verify context xml.");
       if (m_brokerage == null) throw new BeanCreationException("No Brokerage was set. Verify context xml.");
    }
public void setBank(Bank bank) {
       m_bank = bank;
    }

    public void setBrokerage(Brokerage brokerage) {
       m_brokerage = brokerage;
    }

}


More Stories By Frances Zhao

Frances Zhao is a principal product manager in the Oracle Fusion Middleware team. Her focus is on the core J2EE container.

More Stories By Paul Parkinson

Paul Parkinson has been working with and developing transaction processing technology for 15 years. His work at Oracle includes the development of the Java Transaction API and Java Transaction Service implementations in the OC4J application server as well as performance and high-availability features, Web Service Transactions, and transactional aspects of JCA.

Comments (2) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
Guy Pardon 07/24/07 08:58:32 AM EDT

(Trying again - link not property showing in my first post)

A complete working JMS/JDBC application with Spring JTA integration can be found here:

http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-s...

Guy Pardon 07/24/07 08:56:16 AM EDT

Nice article, though the sample application seems a bit exotic (2 databases are being combined in a synchronous, tighly-coupled integration scenario). More common is a loosely-coupled architecture where you have one (JMS) queue and one database - banks are also more likely to work that way.

A complete working JMS/JDBC application with Spring JTA integration can be found here:

www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html

more (and in-depth) info on Spring's transaction configuration options is in following presentation:

http://media.techtarget.com/tss/BeJUG/J2EEAppsSpring/player.html

Disclaimer: I should mention that I am the author of both these publications, and heavily involved in some of the related technologies.

Best
Guy