1 Introduction to Spring Framework Transaction Management
Comprehensive transaction support is a compelling reason to use the Spring Framework. The Spring framework provides consistent abstractions for transaction management, with the following benefits:
Consistent programming model across different transaction APIs, such as Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA) and Java Data Objects ( JDO).
Supports declarative transaction management.
Simpler than complex transaction APIs (e.g., JTA).
Perfectly integrated with Spring’s data access abstraction.
2 Advantages of the Spring Framework’s transaction support model
Traditionally, Java EE developers have two optional transaction management methods: global or local transactions, each with their own limitations.
2.1 Global Transactions
Global transactions allow you to use multiple transaction resources, usually relational databases and message queues. The application manages global transactions via JTA, which is a clunky API. Also, JTA UserTransaction usually needs to come from JNDI, meaning you need to use JNDI. Obviously, using global transactions will limit the reuse of application code, because JTA is usually only available in an application server environment.
In the past, the preferred way to use global transactions was through EJB CMT (Container Managed Transactions): CMT is declarative transaction management. EJB CMT clears the JNDI lookup for related transactions, but EJB itself needs to use JNDI. It eliminates most (but not all) the need to write Java code to control transactions. The important disadvantage is that CMT bundles JTA and application server environments. At the same time, it only works when using EJBs to implement business logic, or at least within a transactional EJB facade.
2.2 Local transactions
Local transactions are specific resources, for example, transactions are associated with JDBC connections. Local transactions are easy to use, but have a significant disadvantage: they cannot span multiple transaction resources. For example, transactions managed using a JDBC connection cannot run within a global JTA transaction. Because the application server is not responsible for transaction management, it does not guarantee correctness across resources. Another disadvantage is the intrusive programming model of local transactions.
2.3 The consistent programming model of the Spring framework
Spring solves the shortcomings of global and local transactions. It allows developers to use a consistent programming model in any environment. Developers only need to write their own code, which abstracts away different transaction management in different environments. The Spring framework provides declarative and programmatic transaction management. Most users prefer declarative transaction management, which is also recommended.
Using programmatic transaction management, developers use Spring framework transaction abstraction and can run on any underlying transaction. Using the preferred declarative model, developers typically write little or no transaction management code and, therefore, do not rely on the Spring Framework transaction API, or other transaction APIs.
3 The key to understanding the Spring framework transaction abstraction
Spring transaction abstraction is the concept of transaction strategy. Transaction strategy is defined through the org.springframework.transaction.PlatformTransactionManager interface:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(
using using using org.springframework.transaction. ‐ ‐ ‐ ‐‐‐‐‐‐‐‐‐‐ void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
This is primarily a Service Provider Interface (SPI), although it can use the programming model in your code. Because PlatformTransactionManager is an interface, it is easy to mock and stubbed. It does not require a lookup strategy like JNDI. PlatformTransactionManager is implemented like other objects defined in the Spring IoC container. This benefit makes Spring Framework transactions worth abstracting, even when using JTA. Transaction code is easier to test than using JTA directly.
The TransactionException thrown by the PlatformTransactionManager interface method is an undetected exception (that is, it inherits java.lang.RuntimeException). Transaction failure is fatal. In the rare cases where application code can actually recover from a transaction failure, the application developer can choose to catch and handle TransactionException. The advantage is that developers don't have to force this.
The getTransaction(..) method relies on the TransactionDefinition parameter to return the TransactionStatus object. The returned TransactionStatus can represent a new transaction, or an existing transaction if the matching transaction exists in the current call stack. In fact, this is the latter case. Just like the Java EE transaction context, TransactionStatus is associated with executable transactions.
TransactionDefinition interface description:
Isolation (transaction isolation level): Transaction isolation level. For example, can this transaction read other uncommitted transactions?
Propagation (transaction propagation): Normally, all code executed in transaction scope will run within the transaction. However, you have an option to specify the behavior of transaction method execution when a transaction context already exists. For example, the code can continue to run in an existing transaction (the usual situation); or the existing transaction can be paused and a new transaction created.
Transaction timeout: How long does the transaction run before it expires and is automatically rolled back through the underlying transaction.
Read-only state: Read-only transactions can be used when your code reads but does not modify data. In some cases, read-only transactions are beneficial for optimization, for example, when you use Hibernate.
These settings reflect standard transaction concepts. These underlying concepts are integral to using the Spring Framework or any transaction management solution.
The TransactionStatus interface provides a simple way for transaction code to control executable transactions and query transaction status:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
Whether you choose declarative or programmatic in Spring For platform transaction management, it is essential to define the correct PlatformTransactionManager implementation. You typically define this implementation via dependency injection. PlatformTransactionManager usually needs to know the working environment: JDBC, JTA, Hibernate, etc. Below is an example of defining a local PlatformTransactionManager. Define JDBC DataSource:If you are using JTA in a Java EE container, then you can get the container's DataSource by using JNDI and Spring's JtaTransactionManager:
JtaTransactionManager does not need to know about the DataSource, or any specific resource, because it uses the container's global transaction management.
You can also use Hibernate’s local transactions. In this case, you need to define Hibernate's LocalSessionFactoryBean, which your application code will use to obtain a Hibernate Session instance.
In this case, the txManager bean is HibernateTransactionManager. Just as the DataSourceTransactionManager needs to reference the DataSource, the HibernateTransactionManager needs to reference the SessionFactory:
hibernate.dialect=${hibernate.dialect}
如果你使用Hibernate和Java EE容器管理JTA事务,那么你只用使用JtaTransactionManager:
在所有这些情况下,应用程序代码不需要改变。你仅仅需要改变配置来改变如何管理事务。
4 使用事务同步资源
现在应该清楚如何创建不同的事务管理器,和它们如何链接需要同步事务的相关资源(例如,DataSourceTransactionManager链接DataSource,HibernateTransactionManager链接SessionFactory等等)。
4.1 高级同步方式
首选方式是使用Spring的高级模板基于持久化集成APIs或使用带有事务的本地ORM APIs——感知工厂bean或代理管理本地资源工厂。这些事务感知解决方案内部处理资源创建、重用、清理、资源事务同步选项和异常映射。因此,数据访问代码没有处理这些任务,但可以关注非样板式持久化逻辑。通常,你使用本地ORM API或通过使用JdbcTemplate获取模板。
4.2 低级同步方式
例如,DataSourceUtils(JDBC)、EntityManagerFactoryUtils(JPA)、SessionFactoryUtils(Hibernate)、PersistenceManagerFactoryUtils(JDO)存在底层同步方式。当你想应用程序代码直接处理本地持久化APIs,你使用这些类确保获取Spring框架管理的实例,事务是(可选)同步的,发生在进程中的异常正确映射一致性API。
例如,在JDBC的情况下,传统的JDBC方式在DataSource上调用getConnection()方法,你可以使用Spring的org.springframework.jdbc.datasource.DataSourceUtils:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果已存在的事务已经有一个连接同步(链接)它,实例被返回。否则,方法调用触发器创建新的连接,(可选)同步任意已存在的事务,后续在相同事务中重用。
这种方式不需要Spring事务管理(事务同步是可选的),因此,无论你是否使用Spring管理事务你都能使用它。
当然,一旦你使用Spring的JDBC支持、JPA支持或Hibernate支持,你通常不喜欢使用DataSourceUtils或其它的帮助类。
4.3 TransactionAwareDataSourceProxy
在底层,已经存在TransactionAwareDataSourceProxy类。这是目标DataSource代理,包装目标DataSource到感知Spring管理事务。为此,它类似于Java EE服务器提供的传统JNDI DataSource。
5 声明式事务管理
Spring框架的声明式事务管理让Spring面向切面编程(AOP)成为可能,尽管,Spring框架发布包以模板形式自带事务切面代码,一般需要理解AOP。
5.1 理解Spring框架的声明式事务实现
通过元数据驱动事务通知(当前基于XML或注解)。AOP联合事务元数据产生AOP代理,使用TransactionInterceptor联合适当的PlatformTransactionManager实现驱动事务环绕方法调用。
5.2 Example of declarative transaction implementation
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" schema/beans/spring-beans.xsd /www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring- aop.xsd"> ; & lt;-& gt; & lt; & lt; ;tx:advice id="txAdvice" transaction-manager="txManager"> tx:method name="get*" read-only="true"/> " execution(* x.y.service.FooService .*(..)) "/& gt; & lt; AOP: Advisor Advice-Ref =" TXADVICE "POINTCUT-Ref =" FooserviceOperation "/& GT; & LT;/AOP: Config & LT & LT & LT ;; bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> ,,,,,,,,,, Causes Spring transaction rollback when a runtime exception or Error is thrown. Checked exceptions do not cause Spring transactions to be rolled back. You can explicitly configure the exception type for transaction rollback. -for="NoProductInStockException"/>
5.6 Using @Transactional
Enable annotation management:
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" beans/spring-beans.xsd .springframework.org/schema/aop http://www.springframework.org/schema/aop/spring- aop.xsd"> & lt; ---- & gt; @Transactional public class DefaultFooService implements FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo) ; void updateFoo(Foo foo); } @Transactional settings: class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ... class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ... Custom abbreviation annotation @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 { } 5.7 Transaction Propagation PROPAGATION_REQUIRED Compared with PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW uses a completely independent transaction. In this case, the underlying physical transaction is different and, therefore, can be committed or rolled back independently, and the rollback status of the outer transaction is not affected by the inner transaction. PROPAGATION_NESTED Use a physics transaction that can roll back to multiple savepoints. This rollback allows the inner transaction scope to trigger a rollback of its scope and the outer transaction to be able to continue the physical transaction even though some operations have been rolled back. This setting is typically mapped to a JDBC savepoint and, therefore, can only be used for JDBC resource transactions. T ProteCted Void DointransactionWithoutResult (TransactionStatus Status) { Try { UpdateOperation1 (); Sexepting ex) { Status.SetrolllLLLLLLLLY (); }}} }); Specify transaction settings: 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); this.transactionTemplate.setIsolationLevel( TransactionDefinition.ISOLATION_READ_UNCOMMITTED); this .transactionTemplate.setTimeout(30); // 30 Seconds } } Configure TransactionTemplate using Spring XML: class="org.springframework.transaction.support.TransactionTemplate"> 6.2 Using PlatformTransactionManager DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { / / Execute business logic } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status); 7 Choose programmatic or declarative management of transactions ? If you only have a small number of transaction operations, choose programmatic transaction management. 8 Transaction Binding Events Starting from Spring 4.2, listener events can be bound to transaction phases. @Component public class MyComponent { @TransactionalEventListener public void handleOrderCreatedEvent(CreationEvent