preface

This article mainly translates official documents. At the same time, it may also add explanations, examples, links and so on where the author thinks it is not clear.

Introduction to Spring Framework transaction Management

Comprehensive transaction support is one of the most compelling reasons to use the Spring Framework. The Spring Framework provides a consistent abstraction for managing transactions, with the following benefits:

  • A consistent programming model across different transaction apis, such as Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA), Java Data Objects (JDO).
  • Support for declarative transaction management.
  • Apis for programmatic transaction management are simpler than complex transaction apis such as JTA.
  • Perfect integration with Spring’s database access abstraction.

The following sections describe the Transaction appreciation and techniques of the Spring Framework. It also discusses best practices, application server integration, and solutions to common problems.)

  • The advantages of the Spring Framework transaction support model describe why the transaction abstraction of Spring Frame is used instead of EJB (Enterprise Java Beans) Container-managed Transactions(CMT,JEE) or drive local Transactions through proprietary apis such as Hibernate.
  • Understanding the Spring Framework transaction abstraction gives an overview of the core classes and describes how to configure various sources and get a DataSource.
  • Synchronizing resources with transactions describes how application code ensures that resources are created, reused, and cleaned up correctly.
  • Declarative transaction Management describes the support for declarative transaction management.
  • Programmatic transaction management covers support for programmatic (that is, display coded) transaction management.
  • Transaction binding events describe how application events are used in transactions.

Benefits of the Spring Framework’s transaction support model

Traditionally, Java EE developers have had two choices for transaction management: global or local transactions, both of which have significant limitations. The next two sections review global and local transaction management, followed by a discussion of how the Spring Framework’s transaction management addresses the limitations of the global and local transaction models.

Global transaction

Global transactions are where you can use multiple data sources, usually relational databases and message queues. Application services manage global transactions through JTA. This is a cumbersome API to use (in part because of its exception model). In addition, JTA UserTransactions typically need to be derived from JNDI(Java Naming and Directory Interface), meaning that you also need to use JNDI in order to use JTA. Obviously using global transactions limits some potential reuse of application code, since JTA is usually only available in the Application Server environment (that is, within this service, transaction control over multiple services (such as SEATA) is not possible).

Previously, the preferred way to use global transactions was through EJB CMT (Container Managed Transaction) :CMT is a form of declarative management (as opposed to programmatic Transaction management) that eliminates the need for transaction-related JNDI lookups, Although using EJB itself of course requires using JNDI. It eliminates most, but not all, of the need to write Java code to control transactions. The obvious disadvantage of CMT is its association with JTA and Application Server. In addition, it needs to be implemented in the EJB or at least after the transactional EJB facade for the business logic to be available. The downside of EJBs in general is so great that this is not an attractive proposition, especially when faced with a compelling alternative to declarative transaction management.

Local transactions

Local transactions are resource-specific, such as those associated with a JDBC connection. Local transactions may be easier to use, but they have an even bigger disadvantage: they cannot be consumed across multiple transactional resources. For example. Code that uses JDBC connection management code cannot be used in global JTA transactions. Because Application Server does not handle transaction management, it is unlikely to help determine correctness across multiple transaction sources. (It is worth noting that most applications use single-transaction resources.) Another disadvantage is that local transactions are intrusive to the programming model.

Spring Framework’s consistent programming model

Spring addresses the shortcomings of both global and local transactions. It enables application developers to use a consistent programming model in any environment. You write code once, and it can benefit from different transaction strategies in different environments. The Spring Framework provides declarative and programmatic transaction management. Most users prefer declarative transaction management, which is recommended in most cases.

With programmatic transaction management, developers use the Spring Frame transaction abstraction, which can be implemented on any transaction management infrastructure. Developers using the more popular declarative pattern typically write little or no code to use transaction management and therefore do not rely on the Spring Framework transaction API and other transaction apis.

Understand the abstraction of Spring Framework transactions

The key to Spring transaction extraction lies in the concept of transaction strategy. Transaction strategy is defined by the org. Springframework. Transaction. The PlatformTransactionManager interface.

public interface PlatformTransactionManager {

    TransactionStatus getTransaction( TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}
Copy the code

This is the main service provider interface (SPI), although it can be used programmatically in your application code. Because the PlatformTransactionManager is an interface, it can easily simulate according to need or stub. It does not rely on lookup strategies like JNDI. PlatformTransactionManager implementation is defined as any other object in the Spring Framework in the IOC container. Even when you use JTA, this benefit alone makes Spring Framework transactions a valuable abstraction. Transaction code can be much easier to test than using JTA directly.

Consistent with the philosophy of the Spring again, unchecked TransactionException can be thrown by any PlatformTransactionManager interface methods. Failure of basic transaction functionality is almost always fatal. In rare cases, application code can actually recover from the failure of a transaction, and the application developer still has the option of catching and handling transactionExceptions. The point is the developers didn’t force it.

getTransaction(..) Method returns a TransactionStatus object based on the TransactionDefinition parameter. The returned TransactionStatus may represent a new transaction or an existing transaction if a matching transaction exists on the call stack. The implication of this latter case is that, like the context in a Java EE transaction, a TransactionStatus is collected in a thread of execution.

The TransactionDefinition interface specifies:

  • Isolation: The degree of work Isolation of this transaction from other transactions. For example, can you read uncommitted writes from other transactions?
  • Propagation: Generally, all the code in a transaction is run in that transaction. However, when a transaction context already exists, you can select the specified behavior by executing the transaction method. For example, code can run continuously in the current transaction; Or an existing transaction can be suspended and a new transaction created. Spring provides everything like EJB CMT. To understand the semantics of transaction propagation in Spring, see Section 16.5.7, “Transaction propagation.”
  • Timeout: How long a transaction can run before it times out, after which the underlying transaction framework automatically rolls back.
  • Read-only State: You can use Read-only transactions when you are read-only and do not modify data. Read-only transactions can be a useful optimization in some situations, such as when you use Hibernate.

The TransactionStatus interface provides a simple way for transaction code to control transaction execution and query status. These concepts should all be familiar, because all transaction apis are common:

public interface TransactionStatus extends SavepointManager {

    boolean isNewTransaction(a);

    boolean hasSavepoint(a);

    void setRollbackOnly(a);

    boolean isRollbackOnly(a);

    void flush(a);

    boolean isCompleted(a);

}
Copy the code

And whatever you choose to use declarative transaction in the spring and programmatic transaction management, define a correct PlatformTransactionManager implementation is absolutely important, what do you usually defined through the dependency injection this implementation.

PlatformTransactionManager implementation usually need to know about their work environment: JDBC, JTA, Hibernate, etc., the following examples show you how to define a local PlatformTransactionManager implementation. (This example applies to plain JDBC).

You define a JDBC 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>
Copy the code

Related PlatformTransactionManager bean definitions refer to the DataSource definition, as follows:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
Copy the code

If you use JTA in a Java EE container, then you can use a DateSource container through JNDI, combined with Spring’s JtaTransactionManager. The JTA and JNDI lookup versions are as follows:

<? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>

    <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/ > <! -- other <bean/> definitions here --> </beans>Copy the code

JtaTransactionManager does not need to know about the DataSource, some specific source, because it uses container global transaction management.

You can easily use Hibernate local transactions, as shown in the following example. In this case, you need to define a Hibernate LocalSessionFactoryBean, which you will use in your code to include the Hibernate Session instance.

The DataSourcebean definition will be similar to defining local JDBC, which has been shown previously and is therefore not shown in the following example.

In case txManager beans belong to the HibernateTransactionManager type. Like DateSourceTransactionManager need a DataSource reference, HibernateTransactionManager need reference SessionFactory.

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.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">
        <value>
            hibernate.dialect=${hibernate.dialect}
        </value>
    </property>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>
Copy the code

If you are using Hibernate and Java EE containers to manage JTA transactions, you should simply use the same JtaTransactionManager as the PREVIOUS JTA JDBC example.

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Copy the code

Synchronization of resources and transactions

Now you should know how to create different transaction management and how they link needs to be synchronized affairs related resources (such as DataSourceTransactionManager synchronous associated a JDBC DataSource, HibernateTransactionManager associated a Hibernate SessionFactory, etc.). This section describes how you should use a persistence AP such as JDBC, Hibernate, or JDO to ensure that these resources are created, reused, and cleaned properly. This section also discusses how to use the related PlatformTransactionManager synchronous trigger (optional) affairs.

Advanced synchronization methods

The preferred approach is to use Spring’s advanced template based on the Persistence integration APIs or use the native ORM APIs with Transaction-Aware Factory Beans or proxy management. These transaction-Aware solutions handle resource creation, reuse, cleaning, optional transaction resource synchronization, and exception mapping internally. So user data access doesn’t have to deal with these tasks and can focus on centralizing non-template persistence logic code. In general, you can use the native ORM API or use a JDBC access template method by using the JdbcTemplate. These processes are detailed in later sections of this reference document.

Low-level synchronization method

Such as DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUti There are low-level classes such as LS (for JDO). When you want to use your application code to directly handle the resource classes of the native persistence APIs, you use these classes to ensure that the appropriate Spring Framework-managed instances, transaction (optional) synchronization, and exceptions that occur during the process are properly mapped to the consistency API.

For example, in the case of JDBC, instead of the traditional JDBC method that calls the getConnection() method on a DataSource, But using Spring org) springframework). The JDBC datasource. DataSourceUtils is as follows:

Connection conn = DataSourceUtils.getConnection(dataSource);
Copy the code

Returns an instance of an existing transaction if it has a synchronous connection. Otherwise, the method call triggers the creation of a new connection that is (optionally) synchronized to any existing transaction and available for subsequent reuse within that transaction. As mentioned earlier, some SQL Exception is contained in the Spring Framework CannotGetJdbcConnectionException, Spring Framework is one of the DataAccessExceptiion without inspection. This approach yields more information than in SQLException and ensures easy access across different databases and even across different persistence technologies.

This approach also works without Spring Transation Management (transaction synchronization is optional), so you can use it with or without Spring Transaction Management.

Of course, once you use Spring’s JDBC support, JPA support, or Hibernate support, you usually don’t use DataSourceUtils or other helper classes because it’s more enjoyable to abstract through Spring than to use the relevant API directly. If you use the Spring JdbcTemplate(internally using DataSourceUtils) or jdbC.object package to simplify your USE of JDBC, the correct connection happens behind the scenes and you don’t need to write any special code.

TransactionAwareDatasourceProxy

In the bottom TransactionAwareDataSourceProxy class. This is an agent for the target DataSource, wraps the target DataSource and adds Spring-managed Transactions awareness. In this respect, it is similar to a transactional JNDI DataSource provided by a Java EE server.

You should rarely need to use this class unless you must call existing code and pass a standard JDBC DataSource interface implementation. In this case, the code might be available, but participate in Spring Managed Transaction. It is better to write your new code using the higher-level abstraction methods mentioned above.

Declarative transaction management

Most Spring Framework consumers declare transaction management. This option has minimal intrusion into the application code, and therefore is most consistent with the non-invasive lightweight container concept.

The declarative management of the Spring Framework is made possible through Spring Aspect-oriented Programming (AOP), although since transaction-aspect code comes with the Release of the Spring Framework and can be used in a boilerboard fashion, AOP concepts generally do not have to be understood to be effective.

The Spring Framework’s transaction management is similar to EJB CMT in that you can specify transaction Behavior (or lack thereof) at the level of a single method. SetRoolbackOnly () can be called in context if necessary. The differences between the two types of transaction management are as follows:

  • Unlike EJB CMT bound to JTA, the Spring Framework’s declarative transactions work in any environment. It can work with JTA transactions or local transactions using JDBC, JPA, Hibernate, or JDO by simply adjusting configuration files.
  • You can apply Spring Framework declarative transaction management to any class, not just a few special classes like EJB.
  • The Spring Framework has no EJB features through declarative rollback rules. Rollback is programmatic or declarative.
  • The Spring Framework uses the ability to customize the behavior of transactions by using AOP. For example, you can insert custom behavior in the case of transaction rollback. You can add any advice as well as transactional advice. With EJB CMT you cannot affect the transaction management of the container other than setRollbackOnly().
  • The Spring Framework does not support propagation across remote invocation transaction contexts, nor do high-end application services. If you need this functionality, we recommend using EJBs. However, you need to think carefully before using such functionality, because typically, you don’t want transactions to be invoked across remote locations.

The rollback concept is important: they make sure you specify which exceptions (and throwables) should cause automatic rollback. You should specify this declaration in configuration rather than in Java code. So, while you can still call setRollbackOnly() on the TransactionStatus object to roll back the current transaction, in most cases you can specify a rule that MyApplicationException must always roll back. The obvious advantage of this option is that business objects are not dependent on the transaction bottom layer. For example, they usually do not need to import Spring transction APIs or other Spring APIs.

While the default behavior of the EJB container is to roll back this transaction automatically on a System exception (usually a runtime exception), But the EJB CMT does not automatically roll back an Application Exception (that is, check exceptions other than java.rmi.RemoteException). While Spring’s default behavior for declarative transaction management follows EJB conventions (which are only automatically rolled back in unchecked exceptions), custom behavior is often useful.

Understand the Spring Framework’s declarative transaction implementation

It is not enough just to tell you a simple to use annotations @ Transactional to annotate your classes, add @ EnabeTransactionManagement configuraction on you, and then expect you to understand how it works. This section explains how the Spring Framework’s declarative transaction infrastructure works when dealing with transaction-related issues.

The most important concept to grasp about declarative transactions in the Spring Framework is that such proxying is supported through AOP, and advice for transactions is driven by metadata (currently XML- or annotation-based). AOP and transaction metadata combination creates an AOP agent (by TransactionInterceptor and a suitable PlatformTransactionManager implementation to drive business around method).

Conceptually, calling a method on a transaction proxy looks like this…

Example declarative transaction implementation

Consider the following interface and its attendant implementation. This example uses the Foo and Bar classes as placeholders so that you can concentrate on the use of transactions rather than on the specific domain model. For example purposes, DefaultFooService classes in each method body thrown in the implementation of the UnsupportOperationException sample is very good; It allows you to see the transaction is created and rollback in return UnsupportOperationException instances.

// the service interface that we want to make transactional

package x.y.service;

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}
Copy the code
// an implementation of the above interface

package x.y.service;

public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
    }

    public Foo getFoo(String fooName, String barName) {
        throw new UnsupportedOperationException();
    }

    public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
    }

    public void updateFoo(Foo foo) {
        throw newUnsupportedOperationException(); }}Copy the code

Assume that the first two methods getFoo(String) and getFoo(String,String) of the FooService interface must be executed in a read-write transaction context. The following configuration is explained in detail in the following paragraphs.

<! -- from the file'context.xml'-- > <? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <! --this is the service object that we want to make transactional -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/ > <! --the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
    <tx:advice id="txAdvice" transaction-manager="txManager"> <! -- the transactional semantics... --> <tx:attributes> <! -- all methods starting with'get' are read-only -->
            <tx:method name="get*" read-only="true"/ > <! --other methods use the default transaction settings (see below) -->
            <tx:method name="*"/> </tx:attributes> </tx:advice> <! -- ensure that the above transactional advice runsfor any execution
        of an operation defined by the FooService interface -->
    <aop:config>
        <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..) )"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> <! -- don't forget the DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </bean> <! -- similarly, don't forget the PlatformTransactionManager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/> </bean> <! -- other <bean/> definitions here --> </beans>Copy the code

Check the previous configuration. You want to make a fooService object transactional. The application transaction semantics are encapsulated in the < TX :advice/> definition. This
definition reads “… All methods that begin with ‘get’ are executed in the context of a readable transaction and all other methods are executed in the default transaction semantics. < tx: advice / > tag in the transaction – the manager is used to drive into affairs PlatformTransactionManager attribute name, in this case, the txManager bean.

This < AOP :config/> definition ensures that transactional advice is defined by txAdvice beans that will execute at the appropriate point in the program. Start by defining a pointcut (named fooServiceOperation) that matches any of the FooService interface methods. You then use an Advisor to associate txAdvice with the pointcut. The execution result of fooServiceoperation indicates that txAdvice will be executed.

The expression defined in the < AOP :pointcut/> element is a pointcut oriented expression; For more details on Spring’s aspect expressions, see Chapter 10, Spring’s Aspect oriented Programming.

A common requirement is to transact the entire service layer. The best way to do this is to make a simple change to the pointcut expression to match any method in your service layer. As follows:

<aop:config>
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..) )"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>
Copy the code

Now that we’ve analyzed the configuration, you might be asking yourself, “Ok… But what do these configurations actually do?” .

The above configuration will be used to create a transaction proxy around the generated object from the fooService Bean definition. This proxy will be configured to use Transactional Advice so that when the appropriate method is invoked on the proxy, a transaction is started, paused, marked readable only, and so on, depending on the transaction configuration associated with the method. Consider the following program to test drive the above configuration.

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
        FooService fooService = (FooService) ctx.getBean("fooService");
        fooService.insertFoo (newFoo()); }}Copy the code

The output is as follows

<! -- the Spring container is starting up... --> [AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxyfor bean 'fooService' with 0 common interceptors and 1specific interceptors <! -- the DefaultFooService is actually proxied --> [JdkDynamicAopProxy] - Creating JDK dynamic proxyfor[x.y.service.DefaultFooService] <! -...the insertFoo(..) method is now being invoked on the proxy -->
[TransactionInterceptor] - Getting transaction forx.y.service.FooService.insertFoo <! -- the transactional advice kicks in here... --> [DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo] [DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]forJDBC transaction <! -- theinsertFoo(..) method from DefaultFooService throws an exception... -->
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback fortransaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException] <! -- and the transaction is rolledback (by default, RuntimeException instances cause rollback) -->
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource

Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)<! -- AOP infrastructure stack trace elements removedfor clarity -->
at $Proxy0.insertFoo(Unknown Source)
at Boot.main(Boot.java:11)
Copy the code

Roll back a declarative transaction

The previous section outlined the basics of how to specify transactional configuration for classes (typically service layer classes) in your application. This section describes how to control the rollback of transactions in a simple way.

The recommended way to do this is through the Spring Framework’s transaction Framework, where the job of a transaction is to be rolled back, usually throwing an Exception in the code currently executing the transaction context. The Spring Framework’s transaction underlying code catches any unhandled exception (when it pops up from the call stack) and determines whether or not to mark the transaction for rollback.

In its default configuration, the Spring Framework’s transaction underlying code only marks a transaction rollback when it catches an exception at runtime that does not need to be checked, that is, when a RuntimeException is thrown. Errors also – by default – causes a rollback. By default, throwing a check exception on a transaction method does not roll back.

You can configure exactly which exception types are used to mark a transaction rollback, including checking for exceptions. The following XML snippet shows how you can configure rollback using the checked, application-specified Exception type.

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
Copy the code

If you don’t want to roll back the transaction when an exception is thrown, you can also specify a “no rollback rule” as follows:

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
Copy the code

When the Spring Framework’s transaction underlying code catches an exception and refers to the configured rollback rules to decide whether to mark the transaction for rollback, the strongest matching rule wins. So the configuration below, besides InstrumentNotFoundException other exceptions will accompany the transaction rollback.

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>
Copy the code

You can also programmatically roll back the required declaration. While simple, this process is quite intrusive and tightly couples your code to the Spring Framework’s transaction Framework:

public void resolvePosition(a) {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmaticallyTransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }}Copy the code

You are strongly encouraged to use declarative rollback methods whenever possible. You can use programmatic rollback if you absolutely need it, but its use is the opposite of implementing a clean POJO-based framework.

Configure different transaction semantics for different beans

Consider a scenario where you have multiple service layer objects and you want to apply completely different transaction configurations to them. You do this by different definitions via the < AOP: Advisor /> element using different pointcut and advice-ref attribute values.

As a point of comparison, first assume that all of your Service layer classes are defined in a root X.y.service package. To make all instance Beans defined in a package (or subpackage) class and named after Service have a default transaction configuration, you would write the following:

<? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:config>

        <aop:pointcut id="serviceOperation"
                expression="execution(* x.y.service.. *Service.*(..) )"/>

        <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> </aop:config> <! -- these two beans will be transactional... --> <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <bean id="barService" class="x.y.service.extras.SimpleBarService"/>

    <!-- ... and these two beans won't --> <bean id="anotherService" class="org.xyz.SomeService"/> <! -- (not in the right package) --> <bean id="barManager" class="x.y.service.SimpleBarManager"/> <! -- (doesn't end in 'Service') -->

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/> </tx:attributes> </tx:advice> <! -- other transaction infrastructure beans such as a PlatformTransactionManager omitted... --> </beans>Copy the code

The following example shows how to configure two different Beans with completely different transaction configurations.

<? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:config>

        <aop:pointcut id="defaultServiceOperation"
                expression="execution(* x.y.service.*Service.*(..) )"/>

        <aop:pointcut id="noTxServiceOperation"
                expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..) )"/>

        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/> </aop:config> <! --this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/ > <! --this bean will also be transactional, but with totally different transactional settings -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/> </tx:attributes> </tx:advice> <! -- other transaction infrastructure beans such as a PlatformTransactionManager omitted... --> </beans>Copy the code

<tx:advice/>configuration

This section summarizes the various transaction configurations that can be specified using the
tag. The default
configuration is:

  • Propagation configuration is REQUIRED
  • The Isolation level is DEFAULT.
  • The Transaction is the read/write.
  • Transaction Timeout Indicates the default timeout period of the underlying Transaction system. If timeout is not supported, none.
  • Any RuntimeException triggers a rollback, and any Checked Exception does not.

You can change these default configurations; The different attributes of the
tag that are nested in the
and
tags are summarized as follows:

Attribute Required ? Default Description
name Yes The name of the method associated with the transaction property. Wildcards () is associated with multiple methods using the same transaction attribute Settings; For example the get, handle*, on*Event, etc
propahation No REQUIRED Transaction propagation behavior.
isolation No DFAULT Transaction isolation level.
timeout No – 1 Transaction timeout value (in seconds)
read-only No false Is the transaction read-only?
rollback-for No Exception(s) triggers rollback use; Segmentation. For example, com. Foo. MyBusinessException ServletException.
no-rollback-for No Exception(s) does not trigger rollback use; Segmentation. For example com. Foo. MyBusinessException ServletException.

Using the @ Transactional

In addition to using XML-based declarative configuration for transactions, you can use an annotation-based approach. Declare transaction semantics directly on Java Source Code closer to the affected code. There is no danger of over-coupling because it is convenient to use transactions on your code.

The ease of using the @Transactional annotation is best illustrated by example, as explained in the text below. Consider the class definition:

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}
Copy the code

When the above POJO is defined as a bean in the Spring IoC container, the bean instance can be configured using a transaction by adding just one transaction:

<! -- from the file'context.xml'-- > <? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <! --this is the service object that we want to make transactional -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/ > <! -- enable the configuration of transactional behavior based on annotations --> <tx:annotation-driven transaction-manager="txManager"/ > <! -- a PlatformTransactionManager is still required --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/> </bean> <! -- other <bean/> definitions here --> </beans>Copy the code

If you want to connect the PlatformTransactionManager transactionManager bean name is name, you can omit the < tx: annotation – driven / > tag in the transaction – manager properties. If you want to dependency injection PlatformTransactionManager bean with any other name, then you must show that use the transaction – the manager attribute, as shown in the previous example.

If you are using a Java based configuration, @ EnableTransactionManagement annotations provide the same support. Simply add it to a @Configuration class. Look at Javadocs in detail.

Method visibility

When using proxies, you should apply the @Transactional annotation only on public methods. If you annotate a protected, private, or package visibility method, no errors are raised, but the annotated method does not show the configured transaction Settings. Consider using AspectJ if you need to annotate non-public methods (see below).

You can use annotations on interface definitions, interface methods, class definitions, or class public methods. However, the existence of the @Transactional annotation alone is not sufficient to activate the Transactional behavior. The @Transactional annotation provides simple metadata that can be consumed by some runtime framework and that @transactional – Aware can use to configure transactions on the appropriate bean. In the previous example, the < TX :annotation-driven/> element turns on the transaction behavior.

Spring recommends that you only use it for specific classes@TransactionalAnnotations (and methods of concrete classes). You can certainly use it on an interface (or interface method)@TransactionalAnnotations, but if you use interface-based proxies, it will only work as you expect. The fact that Java annotations do not inherit from the interface means that you use proxy-target- based proxiesclass="true") or based on braided section (mode="aspectj"), then the broker and orchestration underlying framework is unable to recognize the transaction configuration, and the object is not wrapped in a transaction broker, which is definitely bad.Copy the code
In proxy mode (the default), external method calls are intercepted only if they are entered through a proxy. This means that calling itself, in fact, a method on the target object calling another method on the target object, will not result in an actual proxy at run time even if the calling method is marked as@Transactional. In addition, the proxy must be fully initialized to provide the desired behavior, so you should not rely on this functionality through the proxy, i.e@PostConstruct.Copy the code

Consider using the AspectJ pattern (see the attributes in the table below) if you expect your own invocation to be wrapped transactions as well. In this case, first of all, there’s no agent; Instead the target class is woven (that is, its bytecode is modified) so that @Transactional enters runtime behavior on any type method.

Tx: annotation – driven configuration

XML Attribute Annotation Attribute Default Description
transaction-manager N/A (See TransactionManagementConfigurer javadocs) transactionManager Use transaction management names. Only if the transaction management name is not transactionManager is required, as shown above.
mode mode proxy The default pattern “proxy” handles transactions that the AOP framework proxies for annotated beans (following proxy semantics, as described above, only applies to method calls passed in through the proxy). The other “aspect” pattern instead involves the affected class and Spring’s AspectJ transaction aspect, which modifys the target class bytecode to apply to different kinds of callbacks. AspectJ compilation requires the presence of Spring-Aspects. Jar in the classpath and the enablement of load-time translation (or just-in-time compilation). (See the section called “Spring Configuration” for detailed setup load-time compilation.)
proxy-target-class proxyTargetClass false Only applicable to proxy mode. Used to control what type of transaction proxy is created for classes annotated by @Transactional. If the proxy-target-class attribute is set to true, the class-based proxy is created. If proxy-target-class is false or this property is ignored, the standard JDK interface-based proxy is created. For details on the different types of agents, seeSection 10.6 “Agency Mechanism”).
order order Ordered.LOWEST_PRECEDENCE Defines the order of transaction advice to be applied to beans using the @Transactional annotation. For more information on rules associated with AOP advice order, seeThe section called “Advice ordering”.). Not specifying the ordering means that the AOP subsystem decides the ordering of advice.

@ EnbleTransactionManagement – drive / > < tx: an annotation and only looking for defined in the same application @ Transactional context of beans. This means that if you put the annotation-driven configuration in the webApplicationContext of a DispatcherServlet instead of your services. See section 21.2, “DispatcherServlet”.

When evaluating a method’s transaction setup, the underlying location takes precedence. In the following example, the DefaultService class is annotated to set read-only transactions at this level, but the @trasactional annotation takes precedence over the same annotation that defines transactions on the updateFoo(Foo) method.

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        // do something
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // do something}}Copy the code

@ Transactional configuration

The @Transactional annotation is a way to specify that an interface, class, or method must have Transactional semantics; For example, starting a new read-only transaction when a method is called suspends any existing transaction. The @transactional default configuration is as follows:

Attribute | Type | Description | — — — — — — — – | — – | — – | — — — — — the value | String | specified to use transaction manager optional qualifier. The propagation | enum: propagation | transaction propagation behavior. | isolation | enum: the isolation | | transaction isolation level. The timeout | int | transaction read-only? | the read – only | Boolean | affairs are read-only? | the rollback – for | class object array. Classes must inherit from Throwable. | Exception (s) to trigger a rollback for use; Segmentation. For example com. Foo. MyBusinessException ServletException. | no rollback – for | lass, an array of objects. Class must inherit the Throwable | Exception (s) does not trigger a rollback for use; Segmentation. For example com. Foo. MyBusinessException ServletException. |

Currently you have no explicit control over the transaction name, which means that the transaction name is displayed in a Transaction Monitor, if appropriate (such as WebLogic’s Transaction Monitor) and printed in the log. For declarative transactions, the transaction name is always the fully qualified class name + “. + Method name of the transactionally-advised class. For example, if the BussinessService class handlePayment(..) Way to start a transaction, the transaction would be: com. Foo BusinessService. HandlePayment.

Multiple transaction managers using @Transactional

Most Spring applications require only a single transaction manager, but there may be situations where you need to use multiple independent transaction managers within a single application. @ the attribute value of the Transaction annotations can be selectively specify PlatformTransactionManager logo. This identity can be the name of the Transaction Manager bean or a qualifier value. For example, use in the Java code below

public class TransactionalService {

    @Transactional("order")
    public void setSomething(String name) {... }@Transactional("account")
    public void doSomething(a) {... }}Copy the code

You can combine the following transaction management bean definition of the Application Context.

<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">... <qualifier value="order"/>
    </bean>

    <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">... <qualifier value="account"/>
    </bean>
Copy the code

In this case, the two methods on the two TransactionalServices will run under separate transaction management, distinguished by the “Order” and “Account” identifiers. If you do not find qualified PlatformTransactionManager bean, the default < tx: annotation – driven > target transactionManager bean name will be used.

Custom shortcut annotations

If you find that you need to reuse the same @Transaction attribute in different ways, Spring’s meta-annotation support allows you to customize short annotations for your particular use. For example, define the following annotations

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}
Copy the code

Run the example we wrote in the previous section

public class TransactionalService {

    @OrderTx
    public void setSomething(String name) {... }@AccountTx
    public void doSomething(a) {... }}Copy the code

Here we use syntax to define transaction management qualifiers, but also include Propagation Behaviors, rollback rules, timeouts, etc.

Transaction propagation

This section covers some of the semantics of transaction propagation in Spring. Note that this section is not an introduction to transaction propagation itself; Rather, it illustrates some semantics about transaction propagation in Spring.

In spring-managed transactions, note the difference between physical and logical transactions, and how propagation is set up to apply to this distinction.

Required

PROPAGATION_REQUIRED When the propagation set is PROPAGATION_REQUIRED, a logical transaction scope is created with each method set. Each such logical transaction scope can independently determine the rollback only state, and the external transaction scope can logically be independent of the internal transaction scope. Of course in the standard PROPAGATION_REUIRED behavior, all these scopes will be mapped to the same logical transaction. So setting a rollback only flag in the scope of the internal transaction affects the chances of the transaction actually committing (as you might expect).

However, in cases where the internal transaction scope sets a rollback only flag, the external transaction does not have its own rollback, so this rollback is accidental (triggered silently by the internal transaction scope). To throw out a corresponding UnexceptionRollbackException on this. This is the expected behavior, so the transaction caller is never misled into thinking that a commit is being executed when it is not. So if an internal transaction (unknown to the external caller) silently marks the transaction and only rolls back, the external transaction still invokes commit. Outside the caller to receive a UnexceptionRollbackException to rollback instead of clear.

RequiresNew

PROPAGATION_REQUIRES_NEW and PROPAGATION_REQUIRED, using a completely separate transaction for each affected transaction scope. In this case, the underlying transaction is different so that it can be committed and rolled back independently, and the external transaction is not affected by the rollback state of the internal transaction.

Nested

PROPAGATION_NESTED uses a single savepoint physical transaction with multiple transactions that can be rolled back. This local rollback runs an embedded transaction that triggers a rollback within its scope, and the external transaction is allowed to continue the physical transaction although some operations are rolled back. This setting is normally mapped to JDBC security points and will only take effect if JDBC supports such transactions. Please refer to the Spring’s DataSourceTransactionManager.

Advising transactional operations

Suppose you want to perform transactions and some basic profiling advice. How do you implement this on the
tag of the context?

When you want to call the updateFoo(Foo) method, you want to see the following:

  • The configured profiling aspect starts
  • Perform transactional advice
  • Methods are executed on the advised object
  • Transaction commit
  • The profiling aspect reports the exact duration of the entire transaction method invocation

This one does not cover a detailed explanation of AOP(unless it applies to transactions). Refer to Chapter 10, Faceted Programming for Spring, for a more detailed introduction to AOP and AOP in general.

Here is a simple profiling aspect code discussed above. The order of advice is controlled through the Ordered interface. For a more detailed Advice order, see Advice ordering.

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;

public class SimpleProfiler implements Ordered {

    private int order;

    // allows us to control the ordering of advice
    public int getOrder(a) {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    // this method is the around advice
    public Object profile(ProceedingJoinPoint call) throws Throwable {
        Object returnValue;
        StopWatch clock = new StopWatch(getClass().getName());
        try {
            clock.start(call.toShortString());
            returnValue = call.proceed();
        } finally {
            clock.stop();
            System.out.println(clock.prettyPrint());
        }
        returnreturnValue; }}Copy the code
<? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/ > <! --this is the aspect -->
    <bean id="profiler" class="x.y.SimpleProfiler"> <! --execute before the transactional advice (hence the lower order number) -->
        <property name="order" __value="1"__/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager" __order="200"__/> <aop:config> <! --this advice will execute around the transactional advice -->
            <aop:aspect id="profilingAspect" ref="profiler">
                <aop:pointcut id="serviceMethodWithReturnValue"
                        expression="execution(! void x.y.. *Service.*(..) )"/>
                <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
            </aop:aspect>
    </aop:config>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
    </bean>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
Copy the code

The result of the above configuration is that a fooService bean already has the required profiling and transaction aspect usage order. You can configure any number of additional facets in a similar manner.

The following example has the same effect as the above configuration, but uses pure XNL declarations.

<? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/ > <! -- the profiling advice --> <bean id="profiler" class="x.y.SimpleProfiler"> <! --execute before the transactional advice (hence the lower order number) -->
        __<property name="order" value="1 __"/>
    </bean>

    <aop:config>
        <aop:pointcut id="entryPointMethod" expression="execution(* x.y.. *Service.*(..) )"/ > <! --will execute after the profiling advice (c.f. the order attribute) -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" __order="2 __"/ > <! -- order value is higher than the profiling aspect --> <aop:aspect id="profilingAspect" ref="profiler">
            <aop:pointcut id="serviceMethodWithReturnValue"
                    expression="execution(! void x.y.. *Service.*(..) )"/>
            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
        </aop:aspect>

    </aop:config>
    
     <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/> </tx:attributes> </tx:advice> <! -- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here --> </beans>Copy the code

If you want to use this approach to have profiling advice executed after or before transaction advice, You can change the profiling aspect bean’s Order property value to a higher value than the Transactional Advice’s order value.

Use @Transactional with AspectJ

You can also use the Spring Framework’s @Transactional support outside of Spring Container through AspectJ. To do this, you first use the @Transactional annotation to annotate your class (or your class methods), Then you use defined in the spring – aspects. Org in the jars. Springframework. Transaction. The aspectj. AnnotationTransactionAspect connection of your application. This aspect must be configured using transaction Manager. You can of course use the Spring Framework’s IOC container to dependency inject this aspect. The simplest way is to configure dependency injection into transaction Management aspects using the mode attribute value of the < TX :annotation-driven/> element as an aspect, as described in @Transactional. Because we’re focusing here on running applications outside of the Spring container, we’ll show you how to implement it programmatically.

// construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());

// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
Copy the code

When you use aspects, you must annotate the implementation class (and/or the methods of the class), not the implementation interface of the class. Aspects follow the Java rule that annotations on interfaces cannot be inherited.

The @Transactional annotation ona class specifies the default transaction semantics for execution on any method of the class. The @Transactional annotation ona method overrides the default transaction semantics of the @Transactional annotation ona class, if it exists. Any method can use @Transactional regardless of visibility.

To use AnnotationTransactionAspect weave your application, you must use AspectJ (see the AspectJ development guide) or runtime compiler to build your application. See section 10.8.4, “Runtime Compilation with AspectJ in the Spring Framework.”

Programmatic transaction management

The Spring Framework provides two types of programmatic transaction management:

  • Using TransactionTemplate
  • Directly using the PlatformTransactionManager implementation

The Spring team generally recommends that you use TransactionTemplate for programmatic transaction management. This second option is similar to using the JTA UserTransaction API, although handling exceptions is less cumbersome.

Using TransactionTemplate

TransactionTemplate uses the same approach as other Spring Templates such as JdbcTemplate. It uses a rollback method to free the application code from the transaction resources that must be retrieved and released, and the result is generated in the proxy according to the intent, so developers can write code according to their intent.

As you can see in the example below, using TransactionTemplate clearly works with you in conjunction with Spring’s Transaction Infrastructure and apis. Whether programmatic transaction management is appropriate depends on your development needs is something you must decide for yourself.

The code should be executed in the context of a transaction and will be displayed using the TransactionTemplate, as shown below. As an application developer, you write a TransactionCallback implementation class (usually represented as an anonymous inner class) that contains the code you need to execute in a transaction context. Then you use your custom TransactionCallback to execute(…). Method exposed in TransactionTemplate.

public class SimpleService implements Service {

    // single TransactionTemplate shared amongst all methods in this instance
    private final TransactionTemplate transactionTemplate;

    // use constructor-injection to supply the PlatformTransactionManager
    public SimpleService(PlatformTransactionManager transactionManager) {
        Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public Object someServiceMethod(a) {
        return transactionTemplate.execute(new TransactionCallback() {
            // the code in this method executes in a transactional context
            public Object doInTransaction(TransactionStatus status) {
                updateOperation1();
                returnresultOfUpdateOperation2(); }}); }}Copy the code

If there is no return value, the convenience of using anonymous TransactionCallbackWithoutResult class, as follows:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); }});Copy the code

The callback code can use setRollbackOnly() to roll back the transaction using the supplied TransactionStatus object:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

    protected void doInTransactionWithoutResult(TransactionStatus status) {
        try {
            updateOperation1();
            updateOperation2();
        } catch(SomeBusinessExeption ex) { status.setRollbackOnly(); }}})Copy the code

Custom transaction configuration

You can specify transaction configuration mode, isolation level, timeout, and so on, either using Transactionplate configuration or in configuration. The TransactionTemplate instance has default Settings. The following example shows a custom TransactionTemplate:

public class SimpleService implements Service {

    private final TransactionTemplate transactionTemplate;

    public SimpleService(PlatformTransactionManager transactionManager) {
        Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
        this.transactionTemplate = new TransactionTemplate(transactionManager);

        // the transaction settings can be set here explicitly if so desired
        this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
        this.transactionTemplate.setTimeout(30); // 30 seconds
        // and so forth...}}Copy the code

The following example defines a TransactionTemplate using the Spring XML configuration using a custom setting. The shareTransactionTemplate can be injected into the Service as needed.

<bean id="sharedTransactionTemplate"
        class="org.springframework.transaction.support.TransactionTemplate">
    <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
    <property name="timeout" value="30"/>
</bean>
Copy the code

Finally, the Transactiontemplate class instance is thread-safe, so the instance does not maintain any session state. However, TransactonTemplate maintains configuration state, so while some classes may share TransactionTemplate singletons, if a class needs to use a TransactionTemplate with a different configuration, Then you need to create two different TransactionTemplate instances.

Use the PlatformTransactionManager

You can use the org. Springwork. Transaction. The PlatformTransactionManager directly manage your affairs. Through simple PlatformTransactionManager instance, through use on your bean bean references. Then use TransactionDefinition and TransactionStatus objects to start, roll back, and commit.

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
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);
Copy the code

Choose between programmatic and declarative transaction management

Programmatic transaction management is usually a good idea when you have a small number of transactions. For example, if you have a Web application that requires transactions only for certain update operations, you may not want to set up the transaction broker to use Spring or any other technology. In this case, using TransactionTemplate might be a good approach. Being able to display setting transaction names is also something that can only be done programmatically by managing transactions.

Event binding event

Starting with Spring4.2, transaction listeners can be bound to a certain phase of a transaction. A typical example is handling the event when the transaction completes successfully: allowing the event to be used more flexibly when the result of the current transaction is actually important to listen on.

Registering a regular EventListener is via the @eventlistener annotation. If you need to bind it to the current transaction, please use the @ TransactionalEventListener. Doing so binds the listener to the transaction commit phase by default.

Let’s take an example to illustrate this concept. Suppose a Component issues an order-creation event and we define a listener that should fire after the transaction commits successfully:

@Component
public class MyComponent {

    @TransactionalEventListener
    public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {... }}Copy the code

TransactionalEventListener annotation open a phase properties allow you to customize issues, the stages of the listener bindings. BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, and AFTER_COMPLETION (whether commit or rollback).

If no transaction is running, the listener will not be invoked at all because we cannot comply with the required semantics. However, you can override this behavior by setting the property of the fallbackExecution annotation to true.

Application server-specific inheritance

Spring’s transaction abstraction is typically application service-independent. In addition, Spring’s JtaTransactionManager class can choose to perform JNDI lookups on JTA UserTransaction and TransactionManager objects to automatically detect the location of the latter objects, This location varies by application server. Access running on JTA transactions enhances transaction semantics, particularly transaction suspension support. See JtaTransactionManager Javadoc for details.

Spring’s jta ransactionmanger is the standard choice to run on JavaEE applications and can run on all common services. Advanced features such as transaction pauses are also available on many servers — including GlassFish, JBoss, and Geronimo that don’t require any special configuration. However, to fully support transaction pause and more advanced features, Spring provides special adapters for WebLogic and WebSphere. These adapters are discussed in the following sections.

For standard scenarios, including WebLogic Server and WebSphere, consider using convenient < TX: JTA-Transaction-Manager /> configuration elements. When configured, this element automatically detects the underlying services and selects the best transaction management available to the platform. This means that you don’t have to explicitly configure specific service adapter classes (as described in the following section). Instead, they are selected automatically using the standard JtaTransactionManager as the default backup.

IBM WebSphere

WebSphere 6.1.0.9 and higher, it is recommended to use the Spring is WebSphereUowTransactionmanager JTA transaction management. This particular adapter makes use of IBM’s UOWManager API, available in WebSphere Application Server 6.0.2.19 and later and 6.1.0.9 and later. With this adapter, spring-driven transaction suspension (suspension and recovery caused by PROPAGATION_REQUIRES_NEW) is officially supported by IBM.

Oracle WebLogic server

WebLogic Server version 9.0 or higher, you often use WebLogicJtaTransactionManager instead of the alternative JtaTransactionManager class. This special Weblogic-specific formal JtaTransactionManager subclass supports the full functionality of Spring’s transaction definition in a Weblogic-managed transaction environment, beyond standard JTA semantics: Features include the transaction name, isolation level for each transaction, and the correct recovery transaction in all cases.

Solutions to common problems

Using the wrong transaction management for a particular data source

According to the requirements of the you choose transaction technology and using the correct PlatformTransactionManager implementation. When appropriate, the Spring Framework only provides simple and appropriate abstractions. If you’re using global transactions, You must use the org. Springframework. Transaction. The jta. JtaTransactionManager class (or a application service specific JtaTransactionManager subclass) for all of your transactions. Otherwise, the transaction infrastructure will attempt to perform local transactions on resources such as the container DataSource instance. Such local transactions don’t make sense, and good application servers will treat them as errors.

More resources

Learn more about the Spring Framework’s transaction support:

  • Distributed Transactions in Spring, with and Without XA is a JavaWorld demo where Spring’s David Syer guides you through the seven-tier model of Distributed transactions for Spring applications. Three with XA, four without.
  • Java Transaction Design Strategies is a book from InfoQ that provides a good introduction to Transactions in Java. It also contains parallel examples of how to configure and use transactions through the Spring FrameWork and EJB3.