Spring provides a consistent abstraction for transaction management. This abstraction is one of the most important of Spring's abstractions, and delivers the following benefits:
Provides a consistent programming model across different transaction APIs such as JTA, JDBC, Hibernate, iBATIS Database Layer and JDO.
Provides a simpler, easier to use, API for programmatic transaction management than most of these transaction APIs
Integrates with the Spring data access abstraction
Supports Spring declarative transaction management
Traditionally, J2EE developers have had two choices for transaction management: to use global or local transactions. Global transactions are managed by the application server, using JTA. Local transactions are resource-specific: for example, a transaction associated with a JDBC connection. This choice had profound implications. Global transactions provide the ability to work with multiple transactional resources. (It's worth noting that most applications use a single transaction resource) With local transactions, the application server is not involved in transaction management, and cannot help ensure correctness across multiple resources.
Global transactions have a significant downside. Code needs to use JTA: a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be obtained from JNDI: meaning that we need to use both JNDI and JTA to use JTA. Obviously all use of global transactions limits the reusability of application code, as JTA is normally only available in an application server environment.
The preferred way to use global transactions was via EJB CMT (Container Managed Transaction): a form of declarative transaction management (as distinguished from programmatic transaction management). EJB CMT removes the need for transaction-related JNDI lookups--although of course the use of EJB itself necessitates the use of JNDI. It removes most--not all--need to write Java code to control transactions. The significant downside is that CMT is (obviously) tied to JTA and an application server environment; and that it's only available if we choose to implement business logic in EJBs, or at least behind a transactional EJB facade. The negatives around EJB in general are so great that this is not an attractive proposition, when there are alternatives for declarative transaction management.
Local transactions may be easier to use, but also have significant disadvantages: They cannot work across multiple transactional resources, and tend to invade the programming model. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction.
Spring resolves these problems. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. Spring provides both declarative and programmatic transaction management. Declarative transaction management is preferred by most users, and recommended in most cases.
With programmatic transaction management developers work with the Spring transaction abstraction, which can run over any underlying transaction infrastructure. With the preferred declarative model developers typically write little or no code related to transaction management, and hence don't depend on Spring's or any other transaction API.
The key to the Spring transaction abstraction is the notion of a transaction strategy.
This is captured in the org.springframework.transaction.PlatformTransactionManager interface, shown below:
public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
This is primarily an SPI interface, although it can be used programmatically. Note that in keeping with Spring's philosophy, this is an interface. Thus it can easily be mocked or stubbed if necessary. Nor is it tied to a lookup strategy such as JNDI: PlatformTransactionManager implementations are defined like any other object in a Spring IoC container. This benefit alone makes this a worthwhile abstraction even when working with JTA: transactional code can be tested much more easily than if it directly used JTA.
In keeping with Spring's philosophy, TransactionException is unchecked. Failures of the transaction infrastructure are almost invariably fatal. In rare cases where application code can recover from them, the application developer can still choose to catch and handle TransactionException.
The getTransaction() method returns a TransactionStatus object, depending on a TransactionDefinition parameter. The returned TransactionStatus might represent a new or existing transaction (if there was a matching transaction in the current call stack).
As with J2EE transaction contexts, a TransactionStatus is associated with a thread of execution.
The TransactionDefinition interface specifies:
Transaction isolation: The degree of isolation this transaction has from the work of other transactions. For example, can this transaction see uncommitted writes from other transactions?
Transaction propagation: Normally all code executed within a transaction scope will run in that transaction. However, there are several options specifying behavior if a transactional method is executed when a transaction context already exists: For example, simply running in the existing transaction (the most common case); or suspending the existing transaction and creating a new transaction. Spring offers the transaction propagation options familiar from EJB CMT.
Transaction timeout: How long this transaction may run before timing out (automatically being rolled back by the underlying transaction infrastructure).
Read-only status: A read-only transaction does not modify any data. Read-only transactions can be a useful optimization in some cases (such as when using Hibernate).
These settings reflect standard concepts. If necessary, please refer to a resource discussing transaction isolation levels and other core transaction concepts: Understanding such core concepts is essential to using Spring or any other transaction management solution.
The TransactionStatus interface provides a simple way for transactional code to control transaction execution and query transaction status. The concepts should be familiar, as they are common to all transaction APIs:
public interface TransactionStatus { boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); }
However Spring transaction management is used, defining the PlatformTransactionManager implementation is essential. In good Spring fashion, this important definition is made using Inversion of Control.
PlatformTransactionManager implementations normally require knowledge of the environment in which they work: JDBC, JTA, Hibernate etc.
The following examples from dataAccessContext-local.xml from Spring's jPetStore sample application show how a local PlatformTransactionManager implementation can be defined. This will work with JDBC.
We must define a JDBC DataSource, and then use the Spring DataSourceTransactionManager, giving it a reference to the DataSource.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
The PlatformTransactionManager definition will look like this:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
If we use JTA, as in the dataAccessContext-jta.xml file from the same sample application, we need to use a container DataSource, obtained via JNDI, and a JtaTransactionManager implementation. The JtaTransactionManager doesn't need to know about the DataSource, or any other specific resources, as it will use the container's global transaction management.
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="jdbc/jpetstore"/>> </bean> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
We can use Hibernate local transactions easily, as shown in the following examples from the Spring PetClinic sample application.
In this case, we need to define a Hibernate LocalSessionFactory, which application code will use to obtain Hibernate Sessions.
The DataSource bean definition will be similar to one of the above examples, and is not shown. (If it's a container DataSource it should be non-transactional as Spring, rather than the container, will manage transactions.)
The "txManager" bean in this case is of class HibernateTransactionManager. In the same way as the DataSourceTransactionManager needs a reference to the DataSource, the HibernateTransactionManager needs a reference to the SessionFactory.
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> </props> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
With Hibernate and JTA transactions we could simply use the JtaTransactionManager as with JDBC or any other resource strategy.
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Note that this is identical to JTA configuration for any resource, as these are global transactions, which can enlist any transactional resource.
In all these cases, application code won't need to change at all. We can change how transactions are managed merely by changing configuration, even if that change means moving from local to global transactions or vice versa.
It should now be clear how different transaction managers are created, and how they are linked to related resources which need to be synchronized to transactions (i.e. DataSourceTransactionManager to a JDBC DataSource, HibernateTransactionManager to a Hibernate SessionFactory, etc.). There remains the question however of how the application code directly or indirectly using a persistence API (JDBC, Hibernate, JDO, etc.), ensures that these resources are obtained and handled properly, in terms of proper creation/reuse/cleanup and to trigger (optionally) transaction synchronization via the relevant PlatformTransactionManager.
The preferred approach is to use Spring's highest level persistence integration APIs. These do not replace the native APIs, but do internally handle resource creation/reuse, cleanup, optional transaction synchronization of the resources and exception mapping, so that user data access code doesn't have to worry about these concerns at all, but can concentrate purely on non-boilerplate persistence logic. Generally, the same template approach is followed for all persistence APIs, with classes such as JdbcTemplate, HibernateTemplate, JdoTemplate, etc.. These integration classes are detailed in subsequent chapters of this manual.
At a lower level exist classes such as DataSourceUtils (for JDBC), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO), and so on. When it is preferred for application code to deal directly with the resource types of the native persistence APIs, these classes ensure that proper Spring-managed instances are obtained, transactions are (optionally) synchronized to, and exceptions which happen in the process are properly mapped to a consistent API.
For example, for JDBC, instead of the traditional JDBC approach of calling the getConnection() method on the DataSource, you would instead use Spring's org.springframework.jdbc.datasource.DataSourceUtils class as follows:
Connection conn = DataSourceUtils.getConnection(dataSource);
If an existing transaction exists, and already has a connection synchronized (linked) to it, that instance will be returned. Otherwise, the method call will trigger the creation of a new connection, which will be (optionally) synchronized to any existing transaction, and available for subsequent reuse in that same transaction. As mentioned, this has the added advantage that any SQLException will be wrapped in a Spring CannotGetJdbcConnectionException--one of Spring's hierarchy of unchecked DataAccessExceptions. This gives you more information than can easily be obtained from the SQLException, and ensures portability across databases: even across different persistence technologies.
It should be noted that this will also work fine without Spring transaction management (transaction synchronization is optional), so you can use it whether or not you are using Spring for transaction management.
Of course, once you've used Spring's JDBC support or Hibernate support, you will generally prefer not to use DataSourceUtils or the other helper classes, because you'll be much happier working via the Spring abstraction than directly with the relevant APIs. For example, if you use the Spring JdbcTemplate or jdbc.object package to simplify your use of JDBC, correct connection retrieval happens behind the scenes and you won't need to write any special code.
All these lower level resource access classes are detailed in subsequent chapters of this manual.
At the very lowest level exists the TransactionAwareDataSourceProxy class. This is a proxy for a target DataSource, which wraps that target DataSource to add awareness of Spring-managed transactions. In this respect it is similar to a transactional JNDI DataSource as provided by a J2EE server.
It should almost never be necessary or desireable to use this class, except when existing code exists which must be called and passed a standard JDBC DataSource interface implementation. In this case, it's possible to still have this code be usable, but participating in Spring managed transactions. It is preferable to write your own new code using the higher level abstractions mentioned above.
See the TransactionAwareDataSourceProxy Javadocs for more details.
Spring provides two means of programmatic transaction management:
Using the TransactionTemplate
Using a PlatformTransactionManager implementation directly
We generally recommend the first approach.
The second approach is similar to using the JTA UserTransaction API (although exception handling is less cumbersome).
The TransactionTemplate adopts the same approach as other Spring templates such as JdbcTemplate and HibernateTemplate. It uses a callback approach, to free application code from the working of acquiring and releasing resources. (No more try/catch/finally.) Like other templates, a TransactionTemplate is threadsafe.
Application code that must execute in a transaction context looks like this. Note that the TransactionCallback can be used to return a value:
Object result = tt.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } });
If there's no return value, use a TransactionCallbackWithoutResult like this:
tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
Code within the callback can roll the transaction back by calling the setRollbackOnly() method on the TransactionStatus object.
Application classes wishing to use the TransactionTemplate must have access to a PlatformTransactionManager: usually exposed as a JavaBean property or as a constructor argument.
It's easy to unit test such classes with a mock or stub PlatformTransactionManager. There's no JNDI lookup or static magic here: it's a simple interface. As usual, you can use Spring to simplify your unit testing.
You can also use the org.springframework.transaction.PlatformTransactionManager directly to manage your transaction. Simply pass the implementation of the PlatformTransactionManager you're using to your bean via a bean reference. Then, using the TransactionDefinition and TransactionStatus objects you can initiate transactions, rollback and commit.
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // execute your business logic here } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status);
Spring also offers declarative transaction management. This is enabled by Spring AOP, although, as the transactional aspects code comes with Spring and may be used in a boilerplate fashion, AOP concepts do not generally have to be understood to make effective use of this code..
Most Spring users choose declarative transaction management. It is the option with the least impact on application code, and hence is most consistent with the ideals of a non-invasive lightweight container.It may be helpful to begin by considering EJB CMT and explaining the similarities and differences with Spring declarative transaction management. The basic approach is similar: It's possible to specify transaction behavior (or lack of it) down to individual methods. It's possible to make a setRollbackOnly() call within a transaction context if necessary. The differences are:
Unlike EJB CMT, which is tied to JTA, Spring declarative transaction management works in any environment. It can work with JDBC, JDO, Hibernate or other transactions under the covers, with configuration changes only.
Spring enables declarative transaction management to be applied to any POJO, not just special classes such as EJBs.
Spring offers declarative rollback rules: a feature with no EJB equivalent, which we'll discuss below. Rollback can be controlled declaratively, not merely programmatically.
Spring gives you an opportunity to customize transactional behavior, using AOP. For example, if you want to insert custom behavior in the case of transaction rollback, you can. You can also add arbitrary advice, along with the transactional advice. With EJB CMT, you have no way to influence the container's transaction management other than setRollbackOnly().
Spring does not support propagation of transaction contexts across remote calls, as do high-end application servers. If you need this feature, we recommend that you use EJB. However, don't use this feature lightly. Normally we don't want transactions to span remote calls.
The concept of rollback rules is important: they enable us to specify which exceptions (and throwables) should cause automatic roll back. We specify this declaratively, in configuration, not in Java code. So, while we can still call setRollbackOnly() on the TransactionStatus object to roll the current transaction back programmatically, most often we can specify a rule that MyApplicationException should always result in roll back. This has the significant advantage that business objects don't need to depend on the transaction infrastructure. For example, they typically don't need to import any Spring APIs, transaction or other.
While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it's often useful to customize this.
On our benchmarks, the performance of Spring declarative transaction management exceeds that of EJB CMT.
The usual way of setting up transactional proxying in Spring is via the the use of TransactionProxyFactoryBean to create the transactional proxy. This factory bean is simply a specialized version of Spring's generic ProxyFactoryBean, that, in addition to creating a proxy to wrap a target object, will also always automatically create and attach a TransactionInterceptor to that proxy, reducing boilerplate code. (Note that as with ProxyFactoryBean, you may still specify other interceptors or AOP advice to apply via the proxy).
When using TransactionProxyFactoryBean, you need to first of all specify the target object to wrap in the transactional proxy, via the target attribute.. The target object is normally a POJO bean definition. You must also specify a reference to the relevant PlatformTransactionManager. Finally, you must specify the transaction attributes. Transaction attributes contain the definition of what transaction semantics we wish to use (as discussed above), as well as where they apply. Now let's consider the following sample:
<!-- this example is in verbose form, see note later about concise for multiple proxies! --> <!-- the target bean to wrap transactionally --> <bean id="petStoreTarget"> ... </bean> <bean id="petStore" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="txManager"/> <property name="target" ref="petStoreTarget"/> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean>
The transactional proxy will implement the interfaces of the target: in this case, the bean with id petStoreTarget. (Note that using CGLIB it's possible to transactionally proxy non-interface methods of the target class as well. Set the "proxyTargetClass" property to true to force this to always happen, although it will happen automatically if the target doesn't implement any interfaces. In general, of course, we want to program to interfaces rather than classes.) It's possible (and usually a good idea) to restrict the transactional proxy to proxying only specific target interfaces, using the proxyInterfaces property. It's also possible to customize the behavior of a TransactionProxyFactoryBean via several properties inherited from org.springframework.aop.framework.ProxyConfig, and shared with all AOP proxy factories.
The transaction interceptor will ultimately use an object implementating Spring's TransactionAttributeSource interface to get at the transaction attributes (in the form of TransactionAttribute objects) defining the transaction semantics to be applied to specific methods of specific classes. The most basic way to specify this TransactionAttributeSource instance when creating the proxy is for you to create a bean implementing the TransactionAttributeSource interface (Spring has several implementations), and then directly set the the transactionAttributeSource property of the proxy factory bean to refer to it (or wrap it as an inner bean. Alternately, you may set a text string for this property, and rely on the fact that the pre-registered (by Spring) TransactionAttributeSourceEditor will automatically convert that text string to a MethodMapTransactionAttributeSource instance.
However, as shown in this example, most users will instead prefer to define the transaction attributes by setting the transactionAttributes property. This property has a type of Java.util.Properties, which will then internally be converted to a NameMatchTransactionAttributeSource object.
As can be seen in the above definition, a NameMatchTransactionAttributeSource object holds a list of name/value pairs. The key of each pair is a method or methods (a * wildcard ending is optional) to apply transactional semantics to. Note that the method name is not qualified with a package name, but rather is considered relative to the class of the target object being wrapped. The value portion of the name/value pair is the TransactionAttribute itself that needs to be applied. When specifying it as the Properties value as in this example, it's in String format as defined by TransactionAttributeEditor. This format is:
PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2
Note that the only mandatory portion of the string is the propagation setting. The default transactions semantics which apply are as follows:
Exception Handling: RuntimeExceptions roll-back, normal (checked) Exceptions don’t
Transactions are read/write
Isolation Level: TransactionDefinition.ISOLATION_DEFAULT
Timeout: TransactionDefinition.TIMEOUT_DEFAULT
See the JavaDocs for org.springframework.transaction.TransactionDefinition class for the format allowed for the propagation setting and isolation level setting. The String format is the same as the Integer constant names for the same values.
In this example, note that the value for the insert* mapping contains a rollback rule. Adding -MyCheckedException here specifies that if the method throws MyCheckedException or any subclasses, the transaction will automatically be rolled back. Multiple rollback rules can be specified here, comma-separated. A - prefix forces rollback; a + prefix specifies commit. (This allows commit even on unchecked exceptions, if you really know what you're doing!)
The TransactionProxyFactoryBean allows you to set optional "pre" and "post" advice, for additional interception behavior, using the "preInterceptors" and "postInterceptors" properties. Any number of pre and post advices can be set, and their type may be Advisor (in which case they can contain a pointcut), MethodInterceptor or any advice type supported by the current Spring configuration (such as ThrowsAdvice, AfterReturningtAdvice or BeforeAdvice, which are supported by default.) These advices must support a shared-instance model. If you need transactional proxying with advanced AOP features such as stateful mixins, it's normally best to use the generic org.springframework.aop.framework.ProxyFactoryBean, rather than the TransactionProxyFactoryBean convenience proxy creator.
Note: Using TransactionProxyFactoryBean definitions in the form above can seem overly verbose when many almost identical transaction proxies need to be created. You will almost always want to take advantage of parent and child bean definitions, along with inner bean definitions, to significantly reduce the verbosity of your transaction proxy definitions, as described in Section 6.7, “Concise proxy definitions”.
XML-based transaction attribute sources definitions are convenient, and work in any environment, but if you are willing to commit to a dependency on Java 5+ (JDK 1.5+), you will almost certainly want to consider using Spring's support for transaction Annotations in JDK standard format, as the attribute source instead.
Declaring transaction semantics directly in the Java source code puts the declarations much closer to the affected code, and there is generally not much danger of undue coupling, since typically, code that is deployed as transactional is always deployed that way.
The org.springframework.transaction.annotation.Transactional Annotation is used to indicate that an interface, interface method, class, or class method should have transaction semantics.
@Transactional public interface OrderService { void createOrder(Order order); List queryByCriteria(Order criteria);
Used in bare form, this Annotation specifies that an interface, class, or method must be transactional. Default transaction semantics are read/write, PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT, with rollback on a RuntimeException, but not Exception.
Optional properties of the annotation modify transaction settings.
Table 8.1. Properties of the Transactional Annotation
Property | Type | Description |
---|---|---|
propagation | enum: Propagation | optional propagation setting (defaults to PROPAGATION_REQUIRED) |
isolation | enum: Isolation | optional isolation level (defaults to ISOLATION_DEFAULT) |
readOnly | boolean | read/write vs. read-only transaction (defaults to false, or read/write) |
rollbackFor | array of Class objects, must be derived from Throwable | optional array of exception classes which should cause rollback. By default, checked exceptions do not roll back, unchecked (RuntimeException derived) roll back |
rollbackForClassname | array of String class names. Classes must be derived from Throwable | optional array of names of exception classes which should cause rollback |
noRollbackFor | array of Class objects, must be derived from Throwable | optional array of exception classes which should not cause rollback. |
noRollbackForClassname | array of String class names, must be derived from Throwable | optional array of names of exception classes which should not rollback |
The annotation may be placed before an interface definition, a method on an interface, a class definition, or a method on a class. It may exist on both an element of an interface, and a class which implements that interface. The most derived location takes precedence when evaluating the transaction semantics of a method.
Annotating a class definition:
public class OrderServiceImpl implements OrderService { @Transactional void createOrder(Order order); public List queryByCriteria(Order criteria); }
In the following example, the interface is annotated for read-only transactions, which will thus be the setting used for methods by default. The Annotation on the createOrder method overrides this, setting the transaction to read/write, and specifying that transactions should also (in addition to the defualt rollback rule for RuntimeException) rollback when the DuplicateOrderIdException (presumably a non-checked Exception) is thrown.
@Transactional(readOnly=true) interface TestService { @Transactional(readOnly=false, rollbackFor=DuplicateOrderIdException.class) void createOrder(Order order) throws DuplicateOrderIdException ; List queryByCriteria(Order criteria); }
Note that a class definition which implements this interface may still override these settings on its own class or method elements.
By itself, adding instances of this annotation to interface or class elements will not result in transactional wrapping of the implementation clases. Spring must still be told somehow to create transactional proxies around classes with these annotations.
The key is to take advantage of the org.springframework.transaction.annotation.AnnotationTransactionAttributeSource class, which reads Annotations format transaction attributes from class files. Taking the previous example which uses TransactionProxyFactoryBean, the TransactionAttributes property which specified transaction attributes in text form is replaced by the direct usage of the TransactionAttributeSource property, specifying an AnnotationTransactionAttributeSource.
<bean id="petStore" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="txManager"/> <property name="target" ref="petStoreTarget"/> <property name="transactionAttributeSource"> <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/> </property> </bean>
Since the TransactionAttributeSource property does not need to change at all for each proxy instance, when using parent and child bean definitions to avoid code duplication, the property may just be set on the base, parent definition and forgotten, there is never a need to override it in the child since the attribute source will read the right settings from each class file.
The previous example is still more work than would be ideal. There is in principle no need for XML for each proxy (to point to the target bean) when the annotations in the class files themselves can be used as an indication that a proxy needs to be created for the annotated classes.
A more AOP focused approach allows a small amount of boilerplate XML (used once only, not for each target bean) to automatically ensure that proxies are created for all classes with Transactional annotations in them. Spring AOP was fully detailed in a previous chapter, which you should consult for general AOP documentation, but the key is the use of DefaultAdvisorAutoProxyCreator, a BeanPostProcessor. Because it is a bean post processor, it gets a chance to look at every bean that is created as it is created. If the bean contains the Transactional annotation, a transactional proxy is automatically created to wrap it.
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> <property name="transactionInterceptor" ref="txInterceptor"/> </bean> <bean id="txInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="txManager"/> <property name="transactionAttributeSource"> <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/> </property> </bean>
A number of classes are involved here:
TransactionInterceptor: the AOP Advice, actually intercepts method call and wraps it with a transaction
TransactionAttributeSourceAdvisor: AOP Advisor (holds the TransactionInterceptor, which is the advice, and a pointcut (where to apply the advice), in the form of a TransactionAttributeSource)
AnnotationTransactionAttributeSource: TransactionAttributeSource implementation which provides transaction attributes read from class files
DefaultAdvisorAutoProxyCreator: looks for Advisors in the context, and automatically creates proxy objects which are the transactional wrappers
TransactionProxyFactoryBean is very useful, and gives you full control when wrapping objects with a transactional proxy. Used with parent/child bean definitions and inner beans holding the target, and when Java 5 Annotations are not available as an option, it is generally the best choice for transactional wrapping. In the case that you need to wrap a number of beans in a completely identical fashion (for example, a boilerplate, 'make all methods transactional', using a BeanFactoryPostProcessor called BeanNameAutoProxyCreator can offer an alternative approach which can end up being even less verbose for this simplified use case.
To recap, once the ApplicationContext has read its initialization information, it instantiates any beans within it which implement the BeanPostProcessor interface, and gives them a chance to post-process all other beans in the ApplicationContext. So using this mechanism, a properly configured BeanNameAutoProxyCreator can be used to postprocess any other beans in the ApplicationContext (recognizing them by name), and wrap them with a transactional proxy. The actual transaction proxy produced is essentially identical to that produced by the use of TransactionProxyFactoryBean, so will not be discussed further.
Let us consider a sample configuration:
<beans> <!-- Transaction Interceptor set up to do PROPAGATION_REQUIRED on all methods --> <bean id="matchAllWithPropReq" class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource"> <property name="transactionAttribute" value="PROPAGATION_REQUIRED"/> </bean> <bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="txManager"/> <property name="transactionAttributeSource" ref="matchAllWithPropReq"/> </bean> <!-- One BeanNameAutoProxyCreator handles all beans where we want all methods to use PROPAGATION_REQUIRED --> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <idref local="matchAllTxInterceptor"/> <idref bean="hibInterceptor"/> </list> </property> <property name="beanNames"> <list> <idref local="core-services-applicationControllerSevice"/> <idref local="core-services-deviceService"/> <idref local="core-services-authenticationService"/> <idref local="core-services-packagingMessageHandler"/> <idref local="core-services-sendEmail"/> <idref local="core-services-userService"/> </list> </property> </bean> </beans>
Assuming that we already have a TransactionManager instance in our ApplicationContext, the first thing we need to do is create a TransactionInterceptor instance to use. The TransactionInterceptor decides which methods to intercept based on a TransactionAttributeSource implementing object passed to it as a property. In this case, we want to handle the very simple case of matching all methods. This is not necessarily the most efficient approach, but it's very quick to set up, because we can use the special pre-defined MatchAlwaysTransactionAttributeSource, which simply matches all methods. If we wanted to be more specific, we could use other variants such as MethodMapTransactionAttributeSource, NameMatchTransactionAttributeSource, or AttributesTransactionAttributeSource.
Now that we have the transaction interceptor, we simply feed it to a BeanNameAutoProxyCreator instance we define, along with the names of 6 beans in the ApplicationContext that we want to wrap in an identical fashion. As you can see, the net result is significantly less verbose than it would have been to wrap 6 beans identically with TransactionProxyFactoryBean. Wrapping a 7th bean would add only one more line of config.
You may notice that we are able to apply multiple interceptors. In this case, we are also applying a HibernateInterceptor we have previously defined (bean id=hibInterceptor), which will manage Hibernate Sessions for us.
There is one thing to keep in mind, with regards to bean naming, when switching back and forth between the use of TransactionProxyFactoryBean, and BeanNameAutoProxyCreator. For the former, if the target bean is not defined as an inner bean, you normally give the target bean you want to wrap an id similar in form to myServiceTarget, and then give the proxy object an id of myService; then all users of the wrapped object simply refer to the proxy, i.e. myService. (These are just sample naming conventions, the point is that the target object has a different name than the proxy, and both are available from the ApplicationContext). However, when using BeanNameAutoProxyCreator, you name the target object something like myService. Then, when BeanNameAutoProxyCreator postprocesses the target object and create the proxy, it causes the proxy to be inserted into the Application context under the name of the original bean. From that point on, only the proxy (the wrapped object) is available from the ApplicationContext. When using TransactionProxyFactoryBean with the target specified as an inner bean, this naming issue is not a concern, since the inner bean is not normally given a name.
Programmatic transaction management is usually a good idea only if you have a small number of transactional operations. For example, if you have a web application that require transactions only for certain update operations, you may not want to set up transactional proxies using Spring or any other technology. Using the TransactionTemplate may be a good approach.
On the other hand, if your applications has numerous transactional operations, declarative transaction management is usually worthwhile. It keeps transaction management out of business logic, and is not difficult to configure in Spring. Using Spring, rather than EJB CMT, the configuration cost of declarative transaction management is greatly reduced.
Spring's transaction management capabilities--and especially its declarative transaction management--significantly changes traditional thinking as to when a J2EE application requires an application server.
In particular, you don't need an application server just to have declarative transactions via EJB. In fact, even if you have an application server with powerful JTA capabilities, you may well decide that Spring declarative transactions offer more power and a much more productive programming model than EJB CMT.
You need an application server's JTA capability only if you need to enlist multiple transactional resources. Many applications don't face this requirement. For example, many high-end applications use a single, highly scalable, database such as Oracle 9i RAC.
Of course you may need other application server capabilities such as JMS and JCA. However, if you need only JTA, you could also consider an open source JTA add-on such as JOTM. (Spring integrates with JOTM out of the box.) However, as of early 2004, high-end application servers provide more robust support for XA transactions.
The most important point is that with Spring you can choose when to scale your application up to a full-blown application server. Gone are the days when the only alternative to using EJB CMT or JTA was to write coding using local transactions such as those on JDBC connections, and face a hefty rework if you ever needed that code to run within global, container-managed transactions. With Spring only configuration needs to change: your code doesn't.
Spring's transaction abstraction is generally AppServer agnostic. Additionally, Spring's JtaTransactionManager class, which can optionally perform a JNDI lookup for the JTA UserTransaction and TransactionManager objects, can be set to autodetect the location for the latter object, which varies by AppServer. Having access to the TransactionManager instance does allow enhanced transaction semantics. Please see the JtaTransactionManager Javadocs for more details.
In a WebLogic 7.0, 8.1 or higher environment, you will generally prefer to use WebLogicJtaTransactionManager instead of the stock JtaTransactionManager class. This special WebLogic specific subclass of the normal JtaTransactionManager. It supports the full power of Spring's transaction definitions in a WebLogic managed transaction environment, beyond standard JTA semantics: features include transaction names, per-transaction isolation levels, and proper resuming of transactions in all cases.
Please see the Javadocs for full details.
In a WebSphere 5.1, 5.0 and 4 environment, you may wish to use Spring's WebSphereTransactionManagerFactoryBean class. This is a factory bean which retrieves the JTA TransactionManager in a WebSphere environment, which is done via WebSphere's static access methods. These methods are different for each version of WebSphere.
Once the JTA TransactionManager instance has been obtained via this factory bean, Spring's JtaTransactionManager may be configured with a reference to it, for enhanced transaction semantics over the use of only the JTA UserTransaction object.
Please see the Javadocs for full details.
Developers should take care to use the correct PlatformTransactionManager implementation for their requirements.
It's important to understand how the Spring transaction abstraction works with JTA global transactions. Used properly, there is no conflict here: Spring merely provides a simplifying, portable abstraction.
If you are using global transactions, you must use the Spring org.springframework.transaction.jta.JtaTransactionManager for all your for all your transactional operations. Otherwise Spring will attempt to perform local transactions on resources such as container DataSources. Such local transactions don't make sense, and a good application server will treat them as errors.
In some JTA environments with very strict XADataSource implementations -- currently only some WebLogic and WebSphere versions -- when using Hibernate configured without any awareness of the JTA TransactionManager object for that environment, it is is possible for spurious warning or exceptions to show up in the application server log. These warnings or exceptions will say something to the effect that the connection being accessed is no longer valid, or JDBC access is no longer valid, possibly because the transaction is no longer active. As an example, here is an actual exception from WebLogic:
java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No further JDBC access is allowed within this transaction.
This warning is easy to resolve as described in Section 12.2.10, “Spurious AppServer warnings about the transaction or DataSource no longer being active”.