Introducing Spring s Transaction Abstraction


Introducing Spring's Transaction Abstraction

Spring includes a lightweight transaction infrastructure that provides a rich set of options for transaction management in a wide range of environments. Traditional Java programming using transactions involves making a choice whether to code for local transactions, like JDBC transactions, or to code for global transactions using JTA. This choice has to be made up front, and if you later decide to switch to a different strategy, then you are facing a significant effort in rewriting the transaction management code.

The EJB specification provides container-managed transactions (CMT) that eliminate the need to include code to explicitly manage the transactions. All transaction demarcation is done via deployment descriptors. It works well and is probably the most popular feature provided by EJBs. The downside is that you are now tied to the EJB container. You cannot run your code within a standalone application and still take advantage of the transaction demarcation provided by CMT.

Spring's transaction abstraction framework eliminates these problems by providing the same semantics for local and global transaction managements, within an application server or running in a standalone environment. It also provides the option for declarative transaction management via Spring's AOP framework. The AOP framework — and the IoC container it is typically used in conjunction with — can be used in any runtime environment.

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.

If you need to use multiple transactional resources or non-database resources, then you will need an application server's JTA capability. This does not mean that you have to use EJBs because most JTA features are exposed to the web tier via a JNDI lookup. If you need only JTA, then you could also consider an open source JTA add-on such as JOTM. However, high-end application servers provide more robust support for XA at present. (You should always check your application server's transaction management capabilities, if intending to use distributed transactions, as QOS varies significantly.)

Using Spring's transaction framework reduces the choice of transaction management strategy to a configuration and deployment issue that can be made even after a significant portion of the application is already coded. There are no code changes necessary if you switch the transaction environment from a local one to JTA.

The following list highlights the most important features. Each will be covered in more detail later in this chapter.

  • Programmatic transaction demarcation through direct use of a subclass of PlatformTransactionManager

  • A template style approach using a TransactionTemplate that will handle most of the transaction demarcation

  • Declarative transaction demarcation for POJOs using AOP in the form of a ProxyFactoryBean combined with a TransactionInterceptor

  • Simplified declarative transaction demarcation for POJOs with the one-stop-shop TransactionProxyFactoryBean

  • Automatic proxying of business interfaces using the BeanNameAutoProxyCreator or the DefaultAdvisorAutoProxyCreator

  • Demarcating transactions and specifying transaction attributes using source-level metadata with annotations

Overview of Choices for Transaction Control

When you work with Spring's transaction framework, there are two distinct choices that you have to make.

First you have to pick a strategy for demarcating the transactions in your application. There are two ways of interacting with the transaction manager. You can do this programmatically, either through direct use of the API exposed by the PlatformTransactionManager interface, or through a template approach using the TransactionTemplate. A less intrusive approach is to take advantage of Spring's AOP support and use declarative transaction management with either a ProxyFactoryBean/ TransactionInterceptor combination or the more convenient TransactionProxyFactoryBean.

Next, you have to pick a transaction management strategy in the form of an implementation of PlatformTransactionManager. If you have only a single transactional resource, then you can choose from one of the "single-resource" PlatformTransactionManager implementations: DataSourceTransactionManager, HibernateTransactionManager, JdoTransactionManager, PersistenceBrokerTransactionManager, and JmsTransactionManager. The choice at this point depends on what underlying technology you are using. For J2EE applications running in a server with JTA support, the PlatformTransactionManager implementation to use would be the JtaTransactionManager, unless you have only a single resource, in which case you could still choose one of the "single-resource" transaction managers mentioned previously. The JtaTransactionManager will delegate all transaction management to your application server's JTA implementation.

Following is a table outlining the data access technologies supported by Spring's transaction infrastructure, the appropriate transaction manager implementation, and the framework classes that will automatically enable transaction participation if the application is configured to use declarative transaction management or if it uses Spring's transaction framework programmatically.

Technology

Transaction Manager

Built-in Transaction Support

JDBC

DataSourceTransactionManager JtaTransactionManager

JdbcTemplate and all classes in org.springframework.jdbc. object package

IBATIS

DataSourceTransactionManager JtaTransactionManager

SqlMapClientTemplate and SqlClientTemplate

Hibernate

HibernateTransactionManager JtaTransactionManager

HibernateTemplate and HibernateInterceptor

JDO

JdoTransactionManager JtaTransactionManager

JdoTemplate and JdoInterceptor

Apache OJB

PersistenceBrokerTransactionManager JtaTransactionManager

PersistenceBrokerTemplate

JMS

JmsTransactionManager

JmsTemplate

Important 

Transaction demarcation strategy and choice of transaction manager are independent of each other — a unique Spring capability. Both programmatic and declarative transaction demarcation work equally well with any of the transaction manager choices. You should pick the demarcation strategy that works best with the application you are writing and the transaction management strategy based on the operating environment and data access technology used for your application.

A key distinguishing feature of Spring's transaction abstraction layer is that you don't have to change your application code if you switch your transaction manager. You need only to modify your configuration files. This is a great advantage as your code can be deployed unchanged in both an application server environment and as part of a standalone application.

Figure 6-1 shows an overview of Spring's transaction management features.

image from book
Figure 6-1

Transaction Definition

When you configure your transaction environment, there are several items that you might want to configure differently from what the defaults are. For programmatic transaction management, you use the DefaultTransactionDefinition class that implements the TransactionDefinition. If you are using declarative transaction management, then you will use the DefaultTransactionAttribute that also implements the TransactionDefinition interface in addition to the TransactionAttribute interface. The latter allows you to specify which exceptions will cause a rollback and which ones should result in a commit.

The transaction attributes that you can configure are propagation behavior, isolation level, timeout value, and read-only flag. We will cover each one of these attributes in the sections that follow.

Propagation Behavior

The propagation behavior is the attribute that can be used to define the scope of your transactions and how multiple transactions will interact with each other. The default behavior is defined as PROPAGATION_REQUIRED. The following table describes the different options and you will recognize most of them from the EJB specification.

Propagation Constant

Description

PROPAGATION_REQUIRED

Support a current transaction, create a new one if none exists. Analogous to EJB transaction attribute of the same name. This is typically the default setting of a transaction definition.

PROPAGATION_SUPPORTS

Support a current transaction, execute non-transactionally if none exists. Analogous to EJB transaction attribute of the same name.

PROPAGATION_MANDATORY

Support a current transaction, throw an exception if none exists. Analogous to EJB transaction attribute of the same name.

PROPAGATION_REQUIRES_NEW

Create a new transaction, suspending the current trans- action if one exists. Analogous to EJB transaction attribute of the same name.

PROPAGATION_NOT_SUPPORTED

Execute non-transactionally, suspending the current trans- action if one exists. Analogous to EJB transaction attribute of the same name.

PROPAGATION_NEVER

Execute non-transactionally, throw an exception if a trans- action exists. Analogous to EJB transaction attribute of the same name.

PROPAGATION_NESTED

Execute within a nested transaction if a current transaction exists, or behave like PROPAGATION_REQUIRED otherwise. There is no analogous feature in EJB.

The PROPAGATION_NESTED option is the only one that does not correspond to an attribute in the EJB specification. Nested transactions are not supported by EJBs and because support for them is optional in the JTS specification, most JTA/JTS implementations do not provide support for nested transactions. Spring's support for nested transactions is limited to single resource transaction managers, and you also need a database and JDBC driver that supports the JDBC 3.0 Save Point feature. If a nested transaction is rolled back, it is rolled back only to the save point where the nested transaction started. This includes any transactions that are nested within the transaction that is rolled back.

Isolation

The isolation attribute allows you to override the default isolation setting of your data source. The default varies between data sources but the most common one is READ_COMMITTED. Change this if you need to adjust the level of isolation that your transaction requires. All settings are not valid for all resources. Oracle for instance, supports only READ_COMMITTED and SERIALIZABLE. The EJB specification doesn't provide any means of setting the isolation level for CMT but some application servers provide their own proprietary configuration options for this. The complete set of possible isolation attribute values is shown in the following table.

Isolation Constant

Description

ISOLATION_DEFAULT

Use the default value of the underlying data source.

ISOLATION_READ_UNCOMMITTED

Dirty reads, non-repeatable reads, and phantom reads can occur.

ISOLATION_READ_COMMITTED

Dirty reads are prevented; non-repeatable reads and phantom reads can occur.

ISOLATION_REPEATABLE_READ

Dirty reads and non-repeatable reads are prevented; phantom reads can occur.

ISOLATION_SERIALIZABLE

Dirty reads, non-repeatable reads, and phantom reads are prevented.

Timeout

The number of seconds the transaction is allowed to run. After the timeout value has been reached, the transaction manager will cancel the transaction and roll back all changes. The options are TIMEOUT_DEFAULT or any positive number of seconds. Some resources will not support this, but JTA and various JDBC drivers do support timeouts.

Read-Only

This flag can be set to true to indicate that the transaction does not modify any persistent state. This is more of a hint because not all transactional resources can take advantage of this setting. It's particularly useful when using Hibernate because it tells Hibernate not to detect and flush changes within a read-only transaction.

Transaction Status

The Spring TransactionStatus interface defines a way for transactional code to control transaction execution and query transaction status. You use this interface mostly when using programmatic transaction demarcation, but it can also be used with declarative transaction management. In the latter case it's advisable to avoid using this interface because it creates a dependency on the transaction framework. It's normally better to use exceptions to indicate transaction rollbacks.

public interface TransactionStatus {   boolean isNewTransaction();   void setRollbackOnly();   boolean isRollbackOnly(); }

Transaction Demarcation Strategies

Defining what portions of your program are participating in a transaction and if or how these transactions interact are critical decisions that can affect the reliability and scalability of your application. You will also have to decide whether a programmatic strategy is beneficial. Programmatic demarcation makes your code dependent on the transaction framework whether you are using Spring, JTA, or JDBC transactions. It also tends to pollute your business objects with code that has nothing to do with its core responsibility. Declarative transactions have proven very popular in the case of EJB CMT. Spring gives you the option of using declarative transactions regardless of your choice of using POJOs or EJBs, making the benefits of declarative transaction management available in a wider range of environments, and imposing far less onerous requirements on application code.

Where to Demarcate Transactions

Before we get into the different demarcation strategies, it is important to consider where we should apply transactions in our applications. We recommend applying transactions at the business layer. This allows the business layer to catch any exceptions that caused a rollback and issue the appropriate business-level application exceptions.

We don't want to apply them at the data access level because that would limit the opportunity to reuse data access code with varying transaction requirements. Data access code should not delimit transaction boundaries because it doesn't implement business logic, and atomicity is a business-level concept. Let's say we apply a transaction on a data access method that updates the balance of an account. This would prevent us from reusing this method in a task that transferred data between two accounts. If the subtraction from the first account was already committed, then we could not roll back all changes in the case of the addition to the second account failing. We should instead apply the transaction at the higher business operation level. This would in our case be the "deposit," "withdraw," and "transfer" business method.

One issue with applying transactions in the business layer is that for local transactions that are controlled programmatically, the business logic becomes dependent on the data access technology. If we use JDBC for the data access, then the business logic layer must control the connection object. This is undesirable because we prefer to decouple the business logic from the data access logic. The most commonly applied solution to this dilemma is to use declarative transactions like EJB container-managed transactions (CMT) or Spring's declarative transaction support. An alternate solution is to use a transaction management framework that will encapsulate the transaction logic and hide the detail from the business logic. An example of this is Spring's programmatic transaction management support using a TransactionTemplate.

Programmatic Transaction Demarcation

Although declarative transactions are (deservedly) more popular and decouple your code from the transaction infrastructure, there are times when we want to use a programmatic approach. You might write a conversion program that is not going to be maintained beyond a few months, so the coupling with the transaction framework is not terribly important. In this case it might be simpler to keep everything in the Java code rather than creating a number of configuration files containing the transaction definitions in addition to the Java program. Otherwise, your transaction management might be integrated with business logic, and not best externalized as an aspect.

Direct Programmatic Use of PlatformTransactionManager

We have already seen an example of direct use of the PlatformTransactionManager, but we will show one more example for completeness. This time we use IoC to configure the business class. This allows us to use an implementation that fits the environment we will be running in. For a single data source, DataSourceTransactionManager is the choice, but for a JTA environment we would choose the JtaTransactionManager. It's important to note that the choice of transaction manager does not force us to change the actual code at all. This is one of the advantages of using Spring for this type of processing.

First we'll look at the configuration of our sample:

<!-- The DBCP DataSource --> <bean          destroy-method="close">   <property name="driverClassName">     <value>${jdbc.driverClassName}</value>   </property>   <property name="url"><value>${jdbc.url}</value></property>   <property name="username"><value>${jdbc.username}</value></property>   <property name="password"><value>${jdbc.password}</value></property> </bean>     <!-- The DAO class --> <bean     >   <property name="dataSource">     <ref local="dataSource"/>   </property>  </bean>     <!-- The transactionmanager to use for regular non JTA datasource --> <bean    >   <property name="dataSource">     <ref local="dataSource"/>   </property>  </bean>     <!-- Business Object --> <bean      >   <property name="boxOfficeDao">     <ref local="dao"/>   </property>    <property name="transactionManager">     <ref local="transactionManager"/>   </property>  </bean> 

Next, take a look at the code that will perform the transactional processing. This time we will use the default settings for the DefaultTransactionDefinition, which are PROPAGATION_REQUIRED and no timeout, and the isolation level is the default for the resource:

Reservation reservation = null; TransactionDefinition td = new DefaultTransactionDefinition(); TransactionStatus tranStatus = transactionManager.getTransaction(td); try {   //... some business processing   reservation = bookSeats(performance, reserve, request.getHoldUntil(), cost); } catch (ApplicationException e) {   //... handle exception   transactionManager.rollback(tranStatus);   throw e; } transactionManager.commit(tranStatus); return reservation;

This is equivalent to using JTA directly in your code by obtaining a UserTransaction object reference.

TransactionTemplate

It's usually preferable to apply some IoC principles to your transaction processing very much the same way we saw them applied to JDBC processing via the JdbcTemplate class. The TransactionTemplate allows you to execute code within a transaction without having to code the entire transactional workflow with a try/catch block. The TransactionTemplate will take care of initializing the transaction and also the proper outcome whether it is a rollback or a commit. Your transactional code will run within the TransactionCallback.doInTransaction callback method. The transaction outcome is determined based on how the callback method completes. If your code throws a runtime exception, then the transaction is rolled back. It is also rolled back if your code calls setRollbackOnly on the TransactionStatus object that is passed in to the callback method. Otherwise the transaction is committed. Here is an example using the same application context as in the previous example:

Reservation reservation = null;     TransactionTemplate transactionTemplate =      new TransactionTemplate(transactionManager); // alter any transaction characteristics for the template here if necessary reservation =    (Reservation) transactionTemplate.execute(new TransactionCallback() {     public Object doInTransaction(TransactionStatus status) {       //... some business processing       Reservation result;       try {         result = bookSeats(performance, reserve,           request.getHoldUntil(), cost);       }       catch (RequestedSeatNotAvailableException e) {       throw new WrappedException(e);       }       return result;     }   });     return reservation;

One thing to note about the TransactionTemplate is that we can't throw any checked exceptions from inside the doInTransaction method. We have to use an exception wrapper technique and throw unchecked exceptions because the TransactionTemplate doesn't have a throws clause. This wrapped exception can later be un-wrapped and the original exception re-thrown.

Declarative Transaction Demarcation

The focus from here on will be on declarative transaction demarcation. It provides many benefits and allows you to eliminate any dependencies on the transaction framework from your Java code. It should be regarded as the default approach, unless you have unusual requirements.

There are four specific participants that must be configured to properly interact and provide transaction support. This functionality builds on Spring's AOP features, covered in Chapter 4. The four participants are transaction manager, proxy factory, transaction interceptor, and a set of transaction attributes. The diagram in Figure 6-2 shows their relationships and how they interact with your regular objects used in a typical application.

image from book
Figure 6-2

These participants each come in different flavors that can be combined in many ways to tailor your transaction demarcation strategy to your specific needs. The following sections show and explain the most commonly used variations.

ProxyFactoryBean/Transaction Interceptor

Using the generic Spring AOP ProxyFactoryBean (discussed in Chapter 4) and a transaction interceptor is an appropriate solution when you want as much control over the transaction processing as possible. Because this solution uses a regular AOP ProxyFactoryBean, it allows us to use additional interceptors for other purposes. In the following example, the business object is named boxOfficeTarget to indicate that it is the target object for a proxy object. This is a commonly used naming convention that makes it clear what roles the different objects are playing. If we look at the ProxyFactoryBean that is named boxOffice, we can see that its target is the boxOfficeTarget.

The interceptor is specified as a bean named transactionInterceptor. This interceptor bean has a property called transactionAttributeSource that specifies a MethodMapTransactionAttributeSource containing the fully qualified method names of the target class and the transaction attributes that should be in effect for this transaction. The method names can contain an * as a wildcard either at the end or the beginning of the name. After the method name you can specify propagation behavior, isolation level, timeout value, and a readOnly flag in any order. A timeout value is specified with a special timeout_x syntax where x should be replaced by the positive number of seconds for the timeout. Isolation level and propagation behavior are specified with the constants specified in the TransactionDefinition class. They were also listed in the sections covering these settings earlier.

In addition to the mentioned transaction attributes, you can also specify additional rollback and no- rollback attributes based on an exception class. These attributes indicate the desired behavior in terms of transaction commit/rollback when the specified exception is thrown during the transaction processing. The default rule is to roll back on a runtime exception and commit on any checked exception. This matches the behavior of EJB CMT, but it can easily be changed by specifying rollback attributes (known as rollback rules).

Important 

Rollback rules are a unique — and important — Spring capability that has no counterpart in EJB CMT. They are particularly valuable because they limit the need to call a setRollbackOnly() method to force rollback, which implies dependencies on a transaction management API. Thus they are an essential for a "non-invasive" framework.

The following complete example illustrates the use of rollback rules in the sample application to specify the rollback behavior of the BoxOffice instance in configuration, rather than Java code:

<!-- The DBCP DataSource -->   <bean             destroy-method="close">     <property name="driverClassName">       <value>${jdbc.driverClassName}</value>     </property>     <property name="url"><value>${jdbc.url}</value></property>     <property name="username"><value>${jdbc.username}</value></property>     <property name="password"><value>${jdbc.password}</value></property>   </bean>       <!-- The DAO class -->   <bean   >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>       <!-- The transactionmanager to use for regular non JTA datasource -->   <bean      >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>       <!-- TransactionInterceptor -->   <bean       >     <property name="transactionManager">       <ref bean="transactionManager"/>     </property>     <property name="transactionAttributeSource">       <value> org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS,re adOnly org.springframework.prospring.ticket.service.BoxOffice.allocate*=PROPAGATION_REQUIR ED       </value>     </property>   </bean>         <!-- Transactional proxy for the primary business object -->   <bean           >     <property name="target">       <ref local="boxOfficeTarget"/>     </property>     <property name="proxyInterfaces">       <value>org.springframework.prospring.ticket.service.BoxOffice</value>     </property>     <property name="interceptorNames">       <value>transactionInterceptor</value>     </property>   </bean>         <!-- Business Object -->   <bean       >     <property name="boxOfficeDao">       <ref local="dao"/>     </property>    </bean>

TransactionProxyFactoryBean

This Factory Bean is a good choice for general use. It is easy to configure and provides the functionality most often needed. There are only two beans to define. First there is the transaction manager and second there's the TransactionProxyFactoryBean that contains the proxy, transaction interceptor, and transaction attribute definitions in a single bean definition.

For the transaction attributes, we are using the property transactionAttributes, which takes the form of a property bundle. This translates to a NameMatchTransactionAttributeSource that is similar to the MethodMapTransactionAttributeSource that we saw in the previous section. The biggest difference is that we don't have to use fully qualified method names. We need to specify only the method name itself with or without a leading or trailing * as a wildcard character. Because this attribute source is tied to the proxied class, there really is no need to specify the fully qualified method names anyway. We are dealing with a single class only.

We are defining the bean for the proxied class BoxOfficeImpl inside the target property as an inner bean. This makes this bean definition pretty much self contained and very compact. The interfaces to be proxied are automatically determined. It is also possible to proxy a class via CGLIB by setting the proxyTargetClass property to true:

  <!-- The DBCP DataSource -->   <bean            destroy-method="close">     <property name="driverClassName">       <value>${jdbc.driverClassName}</value>     </property>     <property name="url"><value>${jdbc.url}</value></property>     <property name="username"><value>${jdbc.username}</value></property>     <property name="password"><value>${jdbc.password}</value></property>   </bean>       <!-- The DAO class -->   <bean  >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>       <!-- The transactionmanager to use for regular non JTA datasource -->   <bean      >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>       <!-- Transactional proxy and the primary business object -->   <bean      >     <property name="transactionManager"><ref bean="transactionManager"/></property>     <property name="target">       <bean >         <property name="boxOfficeDao">           <ref local="dao"/>         </property>        </bean>     </property>     <property name="transactionAttributes">       <props>         <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>         <prop key="allocate*">PROPAGATION_REQUIRED</prop>       </props>     </property>   </bean>  

BeanNameAutoProxyCreator

If you have a large number of beans that need to have transactions declared, then the BeanNameAuto ProxyCreator comes in handy. It allows you to rely on the framework automatically providing the proper proxy based on your configuration. You configure your transaction settings once and then all you need to do is to provide the name of the bean to the proxy creator. This is convenient because for each additional bean you need only to add it to the list of beanNames:

  <!-- The DBCP DataSource -->   <bean          destroy-method="close">     <property name="driverClassName">       <value>${jdbc.driverClassName}</value>     </property>     <property name="url"><value>${jdbc.url}</value></property>     <property name="username"><value>${jdbc.username}</value></property>     <property name="password"><value>${jdbc.password}</value></property>   </bean>       <!-- The DAO class -->   <bean  >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>       <!-- The transactionmanager to use for regular non JTA datasource -->   <bean      >     <property name="dataSource">       <ref local="dataSource"/>     </property>    </bean>        <!-- TransactionInterceptor -->   <bean           >     <property name="transactionManager">       <ref bean="transactionManager"/>     </property>     <property name="transactionAttributeSource">       <value> org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS ,readOnly org.springframework.prospring.ticket.service.BoxOffice.allocate*= PROPAGATION_REQUIRED       </value>     </property>   </bean>         <!-- BeanNameAutoProxyCreator --> <bean      >   <property name="interceptorNames">     <value>transactionInterceptor</value>   </property>   <property name="beanNames">     <list>       <idref local="boxOffice"/>     </list>   </property> </bean>       <!-- Business Object --> <bean     >   <property name="boxOfficeDao">     <ref local="dao"/>   </property>  </bean>

Source-Level Metadata Using Commons Attributes

This is a feature inspired by XDoclet and the .NET framework. It allows us to specify transaction attributes directly in the Java code as part of the comment block preceding any class or method definition.

Starting with J2SE 5.0, source-level metadata annotations will be a built-in language feature. The section following this one explains how to use this new feature for transaction demarcation. Currently, the majority of application servers are still using J2SE 1.4 or earlier so we will first provide an example using Jakarta Commons Attributes that can be used with these earlier Java versions.

Start off by annotating the class definition and the method that performs a database update:

 /**   * @@DefaultTransactionAttribute (    *       propagationBehavior=TransactionDefinition.PROPAGATION_    SUPPORTS,    *       readOnly=true )   */ public class BoxOfficeImpl implements BoxOffice {   private BoxOfficeDAO boxOfficeDao;     ...      /**   * @@RuleBasedTransactionAttribute (    *       propagationBehavior=TransactionDefinition.PROPAGATION_REQUIRED )   * @@RollbackRuleAttribute (   *       org.springframework.prospring.ticket.service.ApplicationException.class )   */   public Reservation allocateSeats(ReservationRequest request)       throws RequestedSeatNotAvailableException, NotEnoughSeatsException,              InvalidSeatingRequestException {   }   ...       public Booking[] getAllBookings() {           return boxOfficeDao.getAllBookings();   } }

Next we need to add a metadata compilation step to our build script. Here is an example for Ant:

<property name="commons.attributes.tempdir" value=".atts"/> <path >   <fileset dir=".">     <include name="commons-collections.jar"/>     <include name="commons-attributes-compiler.jar"/>     <include name="xjavadoc-1.0.jar"/>   </fileset> </path> <target name="compileattr"      description="Compile attributes with Jakarta Commons Attributes">   <!-- Bring in Jakarta Commons attribute compilation -->   <taskdef resource="org/apache/commons/attributes/anttasks.properties">     <classpath ref/>   </taskdef>   <!-- Compile to a temp directory:      Commons Attributes will place Java source there. -->   <attribute-compiler destdir="${commons.attributes.tempdir}"     attributepackages=       "org.springframework.transaction;       org.springframework.transaction.interceptor">     <fileset dir="${src.dir}" includes="**/*.java"/>   </attribute-compiler> </target>

The directory defined to hold the Java source generated by Commons Attributes (${commons. attributes.tempdir}) must later be included in the build task that compiles all the Java code for the project. By including the attributepackages attribute specifying the package names of any classes used as attributes, we avoid having to use fully qualified names in the attribute specifications. This makes the attributes much more readable.

Finally, let's look at the application context configuration. We can now remove the transaction demarcation bean declarations from the application context and instead include additional configuration in a separate file; in this example it is named declarativeServices.xml. It makes sense to keep this file separate because it is generic and can be reused in other configurations:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">     <beans>       <!--     This bean is a postprocessor that will automatically apply relevant advisors     to any bean in child factories.   -->   <bean  class=     "org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">   </bean>       <bean      class= "org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"     autowire="constructor">   </bean>       <bean           autowire="byType">   </bean>       <!--     AOP advisor that will provide declarative transaction management      based on attributes.   -->   <bean      class= "org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"     autowire="constructor" >   </bean>       <!--     Commons Attributes Attributes implementation. Replace with another     implementation of org.springframework.metadata.Attributes to source     attributes from a different source.   -->   <bean         />     </beans>

The power of this approach is more apparent when you have many transactional business objects, not just one as in this simple example. You can just add more business objects to the context with no additional configuration needed, beyond the attributes in the business object source.

Our application context definition is now shorter without the specific transaction demarcation declarations. Note that the preceding declarations use the auto wire feature, so all we need to supply in the application context is a PlatformTransactionManager implementation:

<!-- The DBCP DataSource --> <bean          destroy-method="close">   <property name="driverClassName">     <value>${jdbc.driverClassName}</value>   </property>   <property name="url"><value>${jdbc.url}</value></property>   <property name="username"><value>${jdbc.username}</value></property>   <property name="password"><value>${jdbc.password}</value></property> </bean>     <!-- The DAO class --> <bean    >   <property name="dataSource">     <ref local="dataSource"/>   </property>  </bean>     <!-- The transactionmanager to use for regular non JTA datasource --> <bean    >   <property name="dataSource">     <ref local="dataSource"/>   </property>  </bean>     <!-- Business Object --> <bean      >   <property name="boxOfficeDao">     <ref local="dao"/>   </property>  </bean>

Finally, to specify that we want to use both the applicationContext.xml and the declarativeServices.xml, we include the following in web.xml:

<context-param>   <param-name>contextConfigLocation</param-name>     <param-value>       /WEB-INF/applicationContext.xml       /WEB-INF/declarativeServices.xml     </param-value> </context-param>

Source-Level Metadata Using J2SE 5.0 Annotations

Support for Java 5.0 annotations is currently being added to Spring, and should be available by the time you read this.

Spring configuration is very similar to that for Commons Attributes, with the same use of an autoproxy creator. However, there is no additional attribute compilation step, as annotations are a core feature of Java 5.0.

Thus, only the Java source would change. (Unfortunately, it would no longer compile with Java 1.4 or earlier.) Our example might look like this:

@TxAttribute(propagationType=PropagationType.SUPPORTS, readOnly=true) public class BoxOfficeImpl implements BoxOffice {   private BoxOfficeDAO boxOfficeDao; ...     @TxAttribute(propagationType=PropagationType.REQUIRED, rollbackFor=ApplicationException.class)   public Reservation allocateSeats(ReservationRequest request)       throws RequestedSeatNotAvailableException, NotEnoughSeatsException,              InvalidSeatingRequestException {   }       ...       public Booking[] getAllBookings() {           return boxOfficeDao.getAllBookings();   } }

Performance Implications

In case you're wondering, the performance overhead of Spring declarative transaction management is small — usually significantly smaller than that of even a local EJB invocation. See Chapter 15 of J2EE without EJB for some benchmarks against EJB CMT.

Transaction Demarcation Best Practices

We have seen many ways to demarcate transactions, and a common question is "Which approach should I use for my application?"

A good starting point for a small application with a couple of transactional business objects is the TransactionProxyFactoryBean. This option is easy to configure and provides enough flexibility for most needs.

For applications with several business classes that need transaction demarcations, we recommend using a parent/child setup as discussed in Chapter 2 in the "Reusing Bean Definitions" section. You specify a TransactionProxyFactoryBean as an abstract parent bean that has the base definitions. You can then easily reference this parent bean for any class that needs to have transactions applied without repeating all the configuration settings for all the classes.

If you have a really large application with many business objects, then you should consider using an auto-proxying solution. This includes both the BeanNameAutoProxyCreator and the DefaultAdvisorAutoProxyCreator mentioned earlier.

Annotations are going to see increased use once J2SE 5.0 is in widespread use. We believe that at that point, the annotation solutions discussed earlier in this chapter often will replace the other solutions entirely based on external bean configuration files.

Transaction Management Strategies

The choice of transaction management strategy is fairly straightforward. If you access only a single database resource and you don't have any other transactional resources involved, then you can use one of the "single-resource" PlatformTransactionManager implementations. Which one you choose depends on your data access technology. There are a few conventions you have to follow and they are covered in the following section under the respective transaction manager. Using a single resource transaction manager keeps your configuration simpler and you don't need to use an application server that provides a JTA implementation. If at a later point you need to make use of JTA, simply change your configuration files.

Single Resource Transaction Managers

The single-resource transaction managers rely on a Spring utility class to register any application use of a transactional resource. This is necessary to allow transaction managers to manage and synchronize use of the resource within an execution thread. The utility class, responsible for this registration, varies for the different transaction managers and it is described for each individual manager in the sections that follow.

DataSourceTransactionManager

The DataSourceTransactionManager can be used for code that accesses a single database via JDBC. The connection to the database gets associated with the thread and the transaction manager will take care of any commit, rollback, and close calls. As long as the connection is obtained via the DataSourceUtils class, the connection will be bound to the thread for the duration of the transaction.

The JdbcTemplate and all classes in the org.springframework.jdbc.object package automatically use the DataSourceUtils behind the scenes, so any data access using these classes is automatically participating in any Spring-controlled transactions as well as any potential JTA transactions.

Application code not using the Spring classes mentioned should make a call to DataSourceUtils.getConnection to obtain a connection and make a call to DataSourceUtils.closeConnectionIfNecessary to close or return the connection to the connection pool. If there is an active transaction, the framework code will manage the connection and call commit or rollback as required. The following example illustrates this usage:

public class MyDataAccessObject {   private void DataSource dataSource;   public void setDataSource(DataSource ds) {     this.dataSource = ds;   }   public void myDataAccessMethod() {     Connection con = DataSourceUtils.getConnection(this.dataSource);     try {       ...     }     finally {       DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);     }   } }

If you use Spring's JdbcTemplate, you don't have to apply this usage pattern because it is always applied by the JdbcTemplate itself. This is also true if you use any of the RDBMS Operation classes that are built on top of the JdbcTemplate.

Examples of bean declarations for the data access class, the transaction manager, and the data source are as follows:

<beans>   ...   <bean      >     <property name="driverClassName"> ... </property>     <property name="url"> ... </property>     <property name="username"> ... </property>     <property name="password"> ... </property>    </bean>   <bean      >     <property name="dataSource">       <ref bean="myDataSource"/>     </property>   </bean>   <bean  >     <property name="dataSource">       <ref bean="myDataSource"/>     </property>   </bean> </beans>

Both the transaction manager and the data access class have a reference to the data source, and this allows the transaction management to take place as long as the previously mentioned connection lookup pattern is employed.

An alternative to this connection lookup pattern for legacy code that cannot be modified is the use of a TransactionAwareDataSourceProxy class. This class wraps the data source in use and intercepts the getConnection call. It also wraps the connection in a wrapper class to be able to detect any calls to close the connection. This allows this proxy class to provide the functionality of the DataSourceUtils class to check for an active transaction before obtaining a new connection from the data source. Here is an example of how this proxy class would be configured:

<beans>   ...   <bean      >     <property name="targetDataSource">       <ref bean="myDataSourceTarget"/>     </property>   </bean>   <bean      >     <property name="driverClassName"> ... </property>     <property name="url"> ... </property>     <property name="username"> ... </property>     <property name="password"> ... </property>    </bean>   ... </beans> 

The real data source is defined as the target data source and the application will get a reference to the proxy data source.

HibernateTransactionManager

Just as a DataSourceTransactionManager worked with a single DataSource, a HibernateTransactionManager can provide transaction support with a locally defined Hibernate SessionFactory outside of a J2EE environment.

When using Hibernate, the utility class that provides the transaction-aware lookups is SessionFactoryUtils. Use the getSessionFactory method to obtain a new Hibernate session that is associated with a current transaction if one is attached to the thread. When you want to close a session, call the closeSessionIfNecessary method. The HibernateTemplate will handle this lookup pattern, so all this additional code won't be necessary. The code sample that follows shows an example of the lookup that you would have to do if you did not take advantage of the HibernateTemplate:

public class MyDataAccessObject {   private void SessionFactory sessionFactory;   public void setSessionFactory(SessionFactory sf) {   this.sessionFactory = sf;   }   public void myDataAccessMethod() {     Session session = SessionFactoryUtils.getSession(this.sessionFactory);     try {       ...     }     finally {       SessionFactoryUtils.closeSessionIfNecessary(session, this.sessionFactory);     }   } } 

The following bean definition file shows how this example could be configured. In this example, we are using a LocalSessionFactoryBean, which will allow us to use a single database as the data source:

<beans>   ...   <bean       >     <property name="driverClassName"> ... </property>     <property name="url"> ... </property>     <property name="username"> ... </property>     <property name="password"> ... </property>    </bean>       <bean       >     <property name="mappingResources">     <list>       <value>mypackage/hibernate/sample.hbm.xml</value>     </list>     </property>     <property name="hibernateProperties">       <props>           <prop key="hibernate.dialect">          net.sf.hibernate.dialect.MySQLDialect         </prop>       </props>     </property>     <property name="dataSource">       <ref bean="myDataSource"/>     </property>   </bean>       <bean       >     <property name="sessionFactory">       <ref bean="mySessionFactory"/>     </property>   </bean>   <bean  >     <property name="sessionFactory">       <ref bean="mySessionFactory"/>     </property>   </bean> </beans>

Starting from the bottom, we have the MyDataAccessObject with a reference to the SessionFactory. The SessionFactory in turn has a reference to the DataSource, which is also referenced by the TransactionManager.

JDOTransactionManager

The JDOTransactionManager is very similar to the Hibernate version. You must use the getPersistenceManager method from PersistenceManagerFactoryUtils to get the PersistenceManager that will take part in a, potentially existing, transaction and that will allow the JDOTransactionManager to provide all of the transaction management for the PersistenceManager. Once the processing is done, call the closePersistenceManagerIfNecessary method of the same utility class. If you are using the JDOTemplate, then this lookup pattern will automatically be handled by the framework code.

public class MyDataAccessObject {   private void PersistenceManagerFactory persistenceManagerFactory;   public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {     this.persistenceManagerFactory = pmf;   }   public void myDataAccessMethod() {     PersistenceManager pm =          PersistenceManagerFactoryUtils.getPersistenceManager(           this.persistenceManagerFactory);     try {       ...     }     finally {     PersistenceManagerFactoryUtils.closePersistenceManagerIfNecessary(         pm, this.persistenceManagerFactory);     }   } }

The following is an example of the bean configuration that would provide the base for the preceding example:

<beans>   ...   <bean      >     <property name="jdoProperties">       <props>         ...       </props>     </property>   </bean>       <bean      >     <property name="persistenceManagerFactory">       <ref bean="myPersistenceManagerFactory"/>     </property>   </bean>       <bean  >     <property name="persistenceManagerFactory">       <ref bean="myPersistenceManagerFactory"/>     </property>   </bean> </beans>

The data access class has a reference to the PersistenceManagerFactory and so does the JDOTransactionManager.

TopLinkTransactionManager

The Spring/TopLink integration, implemented by the Oracle TopLink team, provides a TopLinkTransactionManager that allows local transactions to be applied with TopLink. (Of course, TopLink can be used with JTA global transactions as well.) The details should be familiar.

See the following article by Jim Clark for further information: www.oracle.com/technology/products/ias/toplink/preview/spring/SpringTopLink.html.

PersistenceBrokerTransactionManager

The last single resource transaction manager that we will cover is the one provided for Apache Object relational Bridge (OJB). OJB provides three different APIs: Persistence Broker, Object Transaction Manager, and ODMG. The Persistence Broker is the lowest level API and that is the API that Spring currently provides support for. Spring provides a PersistenceBrokerTransactionManager that works together with the OjbFactoryUtils for coordinating and managing transactions. To obtain a PersistenceBroker, you should call the getPersistenceBroker method of OjbFactoryUtils. When your processing is done, you should call closePersistenceBrokerIfNecessary. Here is an example of this usage:

public class MyDataAccessObject {   private void PBKey pbKey;   public void setPbKey(PBKey pbk) {     this.pbKey = pbk;   }   public void myDataAccessMethod() {     PersistenceBroker pb =          OjbFactoryUtils.getPersistenceBroker(this.pbKey, false);     try {       ...     }     finally {       OjbFactoryUtils.closePersistenceBrokerIfNecessary(           pb, this.pbKey);     }   } }

Configuration follows along the same lines as JDO and Hibernate where the data access class and the transaction manager both need access to the OJB specific configuration, which is in a class named PBKey:

<beans>   ...   <bean  >     <constructor-arg index="0">       <value>mykey</value>     </constructor-arg>     <constructor-arg index="0">       <value>user</value>     </constructor-arg>     <constructor-arg index="0">       <value>passwd</value>     </constructor-arg>   </bean>       <bean       >     <property name="pbKey">       <ref bean="myPbKey"/>     </property>   </bean>       <bean  >     <property name="pbKey">       <ref bean="myPbKey"/>     </property>   </bean> </beans> 

JtaTransactionManager

So far we have looked only at transaction manager implementations that work with a single database resource. Many applications have to work with other types of resources like JMS queues, or they have to work with multiple resources within a single transaction. For these types of access you must use the JtaTransactionManager, which works with your application server's JTA implementation. You must also use resources that are enabled to work with a JTA implementation. If you have multiple resources for a transaction, then these resources must support the XA interface as well.

Here is a sample configuration for the JtaTransactionManager:

<beans>   ...   <bean      />       <bean  >     ... // properties expecting references to transactional container resources   </bean>     </beans>

There are a few features that you can use with a single resource transaction manager that are not always available in a J2EE environment using a JTA implementation. Here is a summary of Spring/JTA compatibility:

When using Spring's JtaTransactionManager within EJB BMT or web tier components:

  • Everything but transaction suspension will work properly on any J2EE server, as it just touches the JTA UserTransaction, which is covered by standard J2EE and required to be available to application programs.

  • Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) requires the JTA TransactionManager, which is not a public component of standard J2EE. It is, however, a standard JTA interface, defined by the JTA specification. Most application servers make this interface available, and many combine implementations of the TransactionManager and UserTransaction interfaces in the same implementation class.

  • Suspend and resume via the JTA TransactionManager has been tested to work on various J2EE servers. Currently known to work are

    • Resin

    • JBoss 3.x

    • Orion

    • Oracle OC4J

    • JOnAS/JOTM

    • WebSphere 4.0 and 5.x

    • WebLogic 7.0 and 8.1

When using Spring's JtaTransactionManager within EJB CMT:

  • Using direct JTA within EJB CMT is not allowed by the J2EE specification. This applies to Spring's JtaTransactionManager within EJB CMT. To follow the J2EE specification, you should rely on the EJB CMT configuration for all transaction definitions in this environment. In spite of this, we have successfully used Spring-managed transactions in an EJB CMT environment. Alternatively use BMT combined with Spring's transaction framework.

Application Server–Specific JTA Configuration

Vendor-specific lookup of JTA TransactionManager is necessary, as J2EE does not define a standard location for it. By default, we autodetect whether the UserTransaction object implements the TransactionManager interface, which is the case for a surprisingly large number of J2EE servers. If you do not want this behavior, it can be turned off by explicitly setting the autodetectTransactionManager property to false.

WebLogic 7.0 and 8.1 officially supports lookup of both JTA UserTransaction and TransactionManager for EJB BMT and web components. The WebLogicJtaTransactionManager should be used to properly handle resuming a suspended transaction that has been marked for rollback. If you don't need this feature, then the regular JtaTransactionManager will work just as well. For WebLogic 7.0, you should use the WebLogicServerTransactionManagerFactoryBean to look up the TransactionManager. This factory looks up the TransactionManager using an internal WebLogic class to avoid problems related to the implementation returned via the regular JNDI lookup. This workaround is not necessary for WebLogic 8.1.

  <!--  WebLogic 7.0 transaction manager -->   <bean  />   <bean      >     <property name="transactionManager">       <ref local="wls7TM"/>     </property>   </bean>       <!--  WebLogic 8.1 transaction manager -->   <bean        >   </bean> 

WebSphere 4.0, 5.0, and 5.1 use different locations for the TransactionManager lookup. The WebSphereTransactionManagerFactoryBean will check the WebSphere version in use and use the correct lookup. Here is a sample configuration:

  <!--  WebSphere transaction manager -->   <bean  / >   <bean        >       <property name="transactionManager">         <ref local="webSphereTM"/>       </property>   </bean>

Considerations for JTA and O/R Mapping

In order for Spring's JtaTransactionManager to coordinate JTA transactions and O/R mapping tool resource access, the O/R mapping tool has to register with the JTA implementation. For Hibernate, this is done by using Hibernate's JCA Connector. Other O/R mapping solutions such as JDO will have the same type of configuration options and they must be configured for proper participation in the JTA transactions. Check the manual for the respective implementation and for instructions on how to install this.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net