Spring Framework为事务管理提供了一致的抽象,具有以下优势:
跨不同事务API的一致编程模型,例如Java Transaction API(JTA),JDBC,Hibernate,Java Persistence API(JPA)和Java Data Objects(JDO)。
支持声明事务管理支持声明式事务管理。
与复杂的事务API(如JTA)相比,用于编程事务管理的API更简单。
与Spring的数据访问抽象集成。
传统上,Java EE开发人员有两种事务管理选择:全局事务或本地事务,两者都有明显的局限性。
全局事务:JTA、EJB 优点:可以多资源使用; 缺点:JTA API笨重、通过JNDI获取资源。
本地事务:本地事务是资源专用,比如:JDBC连接。 优点:简单易用; 缺点:不能多资源使用。
Spring解决了全局和本地事务的缺点。它使应用程序开发人员能够在任何环境中使用一致的编程模型。Spring Framework提供了声明式和编程式事务管理。大多数用户更喜欢声明式事务管理,在大多数情况下建议使用。大多数用户更喜欢声明式事务管理,在大多数情况下建议使用。
通过编程式事务管理,开发人员可以使用Spring Framework事务抽象,它可以在任何底层事务基础结构上运行。使用首选的声明式模型,开发人员通常很少或根本不编写与事务管理相关的代码,因此不依赖于Spring Framework事务API或任何其他事务API。
Spring事务抽象的关键是事务策略的概念。事务策略由 org.springframework.transaction.PlatformTransactionManager
接口定义:
getTransaction(..)
方法返回 TransactionStatus
对象,具体取决于 TransactionDefinition
参数。 返回的 TransactionStatus
可能表示新事务,或者如果当前调用堆栈中存在匹配的事务,则可以表示现有事务。 后一种情况的含义是,与Java EE事务上下文一样, TransactionStatus
与执行线程相关联。
TransactionDefinition
用于描述事务的隔离级别、超时时间、是否只读事务和事务传播规则等控制事务具体行为的事务属性。
TransactionStatus
接口为事务代码提供了一种控制事务执行和查询事务状态的简单方法。这些概念应该是熟悉的,因为它们对于所有事务API都是通用的:
无论您是在Spring中选择声明式还是程序化事务管理,定义正确的 PlatformTransactionManager
实现都是绝对必要的。 您通常通过依赖注入来定义此实现。
PlatformTransactionManager
实现通常需要了解它们工作的环境:JDBC,JTA,Hibernate等。以下示例显示了如何定义本地 PlatformTransactionManager
实现。 (此示例适用于普通JDBC。)
首先,定义JDBC数据源。
然后,相关的 PlatformTransactionManagerbean
定义将引用DataSource定义。
如果在Java EE容器中使用JTA,则使用通过JNDI获得的容器DataSource以及Spring的JtaTransactionManager。则配置如下:
Spring为不同的持久化框架提供了 PlatformTransactionManager
接口的实现类。如下表所示:
org.springframework.orm.jpa.JpaTransactionManager
:使用JPA进行持久化,使用该事务管理器
org.springframework.orm.hibernateX.HibernateTransactionManager
:使用Hibernate X.0(X可为3,4,5)版本进行持久化时,使用该事务管理器
org.springframework.jdbc.datasource.DataSourceTransactionManager
:使用Spring JDBC或MyBatis等基于DataSource数据源技术的持久化技术时,使用该事务管理器
org.springframework.orm.jdo.JdoTransactionManager
:使用JDO进行持久化时,使用该事务管理器
org.springframework.transaction.jta.JtaTransactionManager
:具有多个数据源的全局事务使用该事务管理器(不管采用何种持久化技术)
本节描述直接或间接使用持久性API(如JDBC,Hibernate或JDO)的应用程序代码如何确保正确创建,重用和清理这些资源。
首选方法是使用Spring基于最高级别模板的持久性集成API,或者将ORM API与事务感知工厂bean或代理一起使用,以管理本地资源工厂。这些事务感知解决方案在内部处理资源创建和重用,清理,资源的可选事务同步以及异常映射。
因此,用户数据访问代码不必解决这些任务,但可以完全专注于非样板持久性逻辑。 通常,您使用本机ORM API或使用模板方法通过使用 JdbcTemplate
进行JDBC访问。
Classes such as DataSourceUtils
(for JDBC), EntityManagerFactoryUtils
(for JPA), SessionFactoryUtils
(for Hibernate), PersistenceManagerFactoryUtils
(for JDO), and so on exist at a lower level. When you want the application code to deal directly with the resource types of the native persistence APIs, you use these classes to ensure that proper Spring Framework-managed instances are obtained, transactions are (optionally) synchronized, and exceptions that occur in the process are properly mapped to a consistent API.
For example, in the case of JDBC,
instead of the traditional JDBC approach of calling the getConnection()
method on the DataSource, you instead use Spring’s org.springframework.jdbc.datasource.DataSourceUtils
class as follows:
If an existing transaction already has a connection synchronized (linked) to it, that instance is returned. Otherwise, the method call triggers the creation of a new connection, which is (optionally) synchronized to any existing transaction, and made available for subsequent reuse in that same transaction. As mentioned, any SQLException
is wrapped in a Spring Framework CannotGetJdbcConnectionException
, one of the Spring Framework’s hierarchy of unchecked DataAccessExceptions. This approach gives you more information than can be obtained easily from the SQLException
, and ensures portability across databases, even across different persistence technologies.
This approach also works without Spring transaction management (transaction synchronization is optional), so you can use it whether or not you are using Spring for transaction management.
大多数Spring Framework用户选择声明式事务管理。此选项对应用程序代码的影响最小,因此最符合非侵入式轻量级容器的理想。
关于Spring Framework的声明式事务支持,最重要的概念是通过AOP代理启用此支持,并且事务切面由元数据(当前基于XML或基于注释)驱动。 AOP与事务元数据的组合产生一个AOP代理,该代理使用 TransactionInterceptor
和适当的 PlatformTransactionManager
实现来驱动围绕方法调用的事务。
从概念上讲,在事务代理上调用方法如下图所示:
假设 FooService
接口的前两个方法 getFoo(String)
和 getFoo(String,String)
必须在具有只读语义的事务的上下文中执行,并且其他方法, insertFoo(Foo)
和 updateFoo(Foo)
,必须在具有读写语义的事务的上下文中执行。则配置文件如下:
<tx:advice/>
标记的 transaction-manager
属性设置为将驱动事务的PlatformTransactionManager bean的名称,在本例中为txManager bean。
<aop:config/>
定义确保txAdvice bean定义的事务切面在程序中的适当位置执行。 首先,定义一个切入点,该切入点与FooService接口(fooServiceOperation)中定义的任何操作的执行相匹配。 然后使用事务切面将切入点与txAdvice相关联。 结果表明,在执行fooServiceOperation时,将运行txAdvice定义的切面。
验证代码如下:
日志信息如下:
向Spring Framework的事务基础结构指示事务的工作将被回滚的推荐方法 是从当前在事务上下文中执行的代码中抛出异常 。 Spring Framework的事务基础结构代码将捕获任何未处理的异常,因为它会使调用堆栈冒泡,并确定是否将事务标记为回滚。
默认配置中,Spring Framework的事务基础结构代码 仅在运行时未经检查的异常情况下标记用于回滚的事务 ; 也就是说,抛出的异常是 RuntimeException
的实例或子类。从事务方法抛出的已检查异常不会导致在默认配置中回滚。
您可以准确配置哪些Exception类型标记用于回滚的事务,包括已检查的异常。以下XML代码段演示了如何为已检查的特定于应用程序的Exception类型配置回滚。
如果您不希望在抛出异常时回滚事务,也可以指定“无回滚规则”。 以下示例告诉Spring Framework的事务基础结构即使面对未处理的InstrumentNotFoundException也要提交事务。
当Spring Framework的事务基础结构捕获异常并参考配置的回滚规则以确定是否将事务标记为回滚时,最强匹配规则将获胜。 因此,在以下配置的情况下,除 InstrumentNotFoundException
之外的任何异常都会导致后续事务的回滚。
还可以以编程方式指示所需的回滚。虽然非常简单,但这个过程非常具有侵入性,并且将您的代码紧密地耦合到Spring Framework的事务基础结构中:
除了基于XML的事务配置声明方法之外,您还可以使用基于注释的方法。
当上面的POJO被定义为Spring IoC容器中的bean时,可以通过仅添加一行XML配置来使bean实例成为事务性的:
如果要连接的PlatformTransactionManager的bean名称具有名称transactionManager,则可以省略标记中的transaction-manager属性。 如果要依赖注入的PlatformTransactionManager bean具有任何其他名称,则必须显式使用transaction-manager属性,如前面的示例所示。
如果使用基于Java的配置,则@EnableTransactionManagement批注提供等效支持。 只需将注释添加到@Configuration类即可。
使用代理时,应仅将@Transactional注释应用于具有公共可见性的方法。 如果使用@Transactional注释对带保护的,私有的或包可见的方法进行注释,则不会引发错误,但带注释的方法不会显示已配置的事务设置。 如果需要注释非公共方法,请考虑使用AspectJ。
可以在接口定义,接口上的方法,类定义或类的公共方法之前放置@Transactional注释。 但是,仅仅存在 @Transactional
注释不足以激活事务行为。 @Transactional注释只是元数据,可由 @Transactional
-aware的某些运行时基础结构使用,并且可以使用元数据来配置具有事务行为的适当bean。
如果您希望自我调用也包含在事务中,请考虑使用AspectJ模式。 在这种情况下,首先不会有代理; 相反,目标类将被编织(即,它的字节代码将被修改),以便在任何类型的方法上将 @Transactional
转换为运行时行为。
大多数Spring应用程序只需要一个事务管理器,但在某些情况下,您可能需要在单个应用程序中使用多个独立的事务管理器。 @Transactional
注释的 value
属性可用于选择性地指定要使用的 PlatformTransactionManager
的标识。
如果您发现许多不同方法在@Transactional上重复使用相同的属性,那么Spring的元注释支持允许您为特定用例定义自定义快捷方式注释。例如,定义以下注释:
应用自定义注释到 TransactionalService
,如下:
Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。 TransactionDefinition
接口中规定了7种类型的事务传播行为,如下:
PROPAGATION_REQUIRED
如果当前没有事务,则新建一个事务;如果已经存在一个事务,则加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS
支持当前事务。如果当前没有事务,则以非事务方式执行。
PROPAGATION_MANDATORY
使用当前事务。如果当前没有事务,则抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务。如果当前存在事务,则把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行。如果当前存在事务,则抛出异常。
PROPAGATION_NEVER
以非事务方式执行。如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
在Spring管理的事务中,请注意物理和逻辑事务之间的区别,以及传播设置如何应用于此差异。
PROPAGATION_REQUIRED
当传播设置为 PROPAGATION_REQUIRED
时,将为应用该设置的每个方法创建逻辑事务范围。每个这样的逻辑事务范围可以单独设置仅回滚状态,外部事务范围在逻辑上独立于内部事务范围。当然,在标准 PROPAGATION_REQUIRED
行为的情况下,所有这些范围将映射到同一物理事务。因此,内部事务范围中的回滚标记确实会影响外部事务实际提交的机会。
但是,在内部事务作用域设置仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务作用域静默触发)是意外的。此时抛出相应的 UnexpectedRollbackException
。这是预期的行为,因此事务的调用者永远不会被误导,假设在实际上没有执行提交。因此,如果内部事务(外部调用者不知道)以静默方式将事务标记为仅回滚,则外部调用者仍会调用 commit
。外部调用者需要接收 UnexpectedRollbackException
以清楚地指示已执行回滚。
与 PROPAGATION_REQUIRED
相比,PROPAGATION_REQUIRES_NEW对每个受影响的事务范围使用完全独立的事务。 在这种情况下,底层物理事务是不同的,因此可以独立提交或回滚,外部事务不受内部事务的回滚状态的影响。
PROPAGATION_NESTED
使用具有多个保存点的单个物理事务,它可以回滚到该事务。 这种部分回滚允许内部事务作用域触发其作用域的回滚,外部事务能够继续物理事务,尽管已经回滚了一些操作。 此设置通常映射到JDBC保存点,因此仅适用于JDBC资源事务。
Suppose you want to execute both transactional and some basic profiling advice. How do you effect this in the context of <tx:annotation-driven/>
?
When you invoke the updateFoo(Foo)
method, you want to see the following actions:
Configured profiling aspect starts up.
Transactional advice executes.
Method on the advised object executes.
Transaction commits.
Profiling aspect reports exact duration of the whole transactional method invocation.
Here is the code for a simple profiling aspect discussed above. The ordering of advice is controlled through the Ordered
interface.
The following example effects the same setup as above, but uses the purely XML declarative approach.
从Spring 4.2开始,事件的监听器可以绑定到事务的一个阶段。 典型的例子是在事务成功完成时处理事件:当 当前事务的结果 对于监听器实际上很重要时,这允许更灵活地使用事件。
注册常规事件侦听器是通过 @EventListener
注释完成的。如果需要将其绑定到事务,请使用 @TransactionalEventListener
。执行此操作时,默认情况下,侦听器将绑定到事务的提交阶段。
TransactionalEventListener
注释提供了一个阶段属性,该属性允许自定义侦听器应绑定到的事务的哪个阶段。 有效阶段是 BEFORE_COMMIT
, AFTER_COMMIT
(默认值), AFTER_ROLLBACK和AFTER_COMPLETION
,它们聚合事务完成(无论是提交还是回滚)。 如果没有正在运行的事务,则根本不调用侦听器,因为我们无法遵守所需的语义。 但是,可以通过将注释的 fallbackExecution
属性设置为 true
来覆盖该行为。
Spring官方文档
Spring4.2新特性(一)