Spring框架其中的一个优点是它全面的事务支持,其好处有:
- 为复杂的事务API提供了一致的编程模型,如JTA、JDBC、Hibernate、JPA和JDO
- 支持声明式事务管理
- 更易于使用的编程式事务管理API
- 很好的整合Spring的各种数据访问抽象
传统来说,J2EE有两个事务有两种选择: 全局和本地 .
全局事务由应用服务器管理,使用JTA。
其主要限制在于, 通常需要将JTA、JNDI同时使用,因为通常JTA的UserTransaction是通过JNDI获得的。
局部事务和资源相关的,比如和一个JDBC链接关联的事务。
其限制在于它们不能同事用于多个事务性资源。例如,使用JDBC连接事务管理的代码不能用于全局的JTA事务中。另外一个缺点是 局部事务趋向于入侵式编程模型。
Spring解决了这方面的问题,它让开发者能够使用在 任何环境下使用一致的编程模型。它同时提供声明式和编程式事务管理。 事务管理是多数使用者的首选,推荐使用!
局部事务的定义
通常,我们需要先定义一个DataSource,然后使用Spring的DataSourceTransactionManager,并传入指向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>
|
再定义一个 事务管理 类:
<!-- 事务管理 -->
<bean id= "txManager"
class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name= "dataSource" ref= "dataSource" />
</bean>
|
Spring解决了这方面的问题,它让开发者能够使用在 任何环境下使用一致的编程模型。它同时提供声明式和编程式事务管理。 事务管理是多数使用者的首选,推荐使用!
Spring事务抽象的关键是 事务策略的概念 ,
主要通过org.springframework.transaction.PlatformTransactionManager接口定义
这里需要说一下 TransactionStatus对象,它代表一个新的或已经存在的事务.
另外,TransactionDefinition接口指定:
- 事务隔离: 当前事务和其他事务的隔离的程度。 事务能否看到其他事务未提交的写数据?
- 事务传播: 通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性的执行行为;例如;挂起现有事务,创建一个新的事务。
- 事务超时: 事务在超时前能运行多久? (自动被底层事务基础设施回滚)
- 只读状态: 只读事务不修改任何数据,只读事务在某些情况下,是非常有用的优化。
下面,做个解释一下,上面的几个特点:
事务隔离级别
隔离级别是指若干并发事务之间的隔离程度。
TransactionDefinition接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT:
默认值,使用底层数据库隔离级别。大部分数据库来说 这通常是
TransactionDefinition.ISOLATION_READ_COMMITED
- TransactionDefinition.ISOLATIOIN_READ_UNCOMMITTED:
该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。其不能防止脏读和不可重复读,
较少使用
- TransactionDefinition.ISOLATION_READ_COMMITTED:
该隔离级别表示一个事务只能读取另一个事务已经提交的数据。其可以防止脏读,推荐使用
- TransactionDefinition.ISOLATION_REPEATABLE_READ:
该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的数据也会被忽略。 该级别防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:
所有事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为
所谓事务传播行为是指, 如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
TransactionDefinition定义中包括了如下几个表示传播行为的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:
如果当前存在事务,则加入该事务;如果没有当前事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:
以非事务方式运行,如果当前存在事务,则抛出异常
- TransactionDefinition.PROPAGATION_MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED.
上面的前6项,都是从EJB引入的。 他们共享相同的概念。 而PROPAGATION_NESTED是Spring 所特有的。以PROPAGATION_NESTED启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。
其类似于JDBC的SavePoint的概念,嵌套事务的子事务就是保存点的一个应用,一个事务中包括多个保存点,每一个嵌套子事务。 另外,外部事务的回滚也导致嵌套事务的回滚。
事务超时
所谓事务超时,指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在TransactionDefinition中以int的值来表示超时时间,单位是秒。
事务的只读属性
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。 所谓事务性资源就是指那些被事务管理的资源,比如数据源、JMS资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在TransactionDefinition中以boolean类型来表示改事务是否只读。
Spring事务管理API分析
在Spring 框架中事务管理的API最重要的有三个: TransactionDefinition、PlatformTransactionManager、TransactionStatus. 事务管理可以理解为: "按照给定的事务规则来执行提交或者回滚操作"!
"给定的事务规则"就是用TransactionDefinition表示的, "按照......来执行或回滚操作"便是用PlatformTransactionManager来表示,而TransactionStatus 用于表示一个运行着的事务的状态。打一个不恰当的比喻,TransactionDefinition与TransactionStatus的关系就像是程序和进程的关系.
编程式事务管理
编程式事务管理我们需要在代码中显式的调用beginTransaction()、commit() 、rollback()等事务管理相关的方法,这就是编程式事务管理。 通过Spring 提供的事务管理API,我们可以在代码中灵活的控制事务执行。而在底层,Spring仍然将事务操作委托给底层的持久化框架来执行。
小例子:
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
public boolean transfer(Long fromId, Long toId, double amount) {
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false ;
try {
result = bankDao.transfer(fromId,toId,amount);
txManager.commit(txStatus);
} catch (Exception e) {
result = false ;
txManager.rollback(txStatus);
}
return result;
}
}
|
配置文件:
<bean id= "bankService" class = "footmark.spring.core.tx.programmatic.origin.BankServiceImpl" >
<property name= "bankDao" ref= "bankDao" />
<property name= "txManager" ref= "transactionManager" />
<property name= "txDefinition" >
<bean class = "org.springframework.transaction.support.DefaultTransactionDefinition" >
<property name= "propagationBehaviorName" value= "PROPAGATION_REQUIRED" />
</bean>
</property>
</bean>
|
我们在类中增加了两个属性:
一个是 TransactionDefinition 类型的属性,它用于定义一个事务;
另一个是 PlatformTransactionManager 类型的属性,用于执行事务管理操作。
基于TransactionTemplate的编程式事务管理
上面的方式很容易理解,但是有一个问题,就是事务处理的代码散落在各个类中,破坏了原有代码的条理性,我们可以在Spring使用模板回调模式
public class BankServiceImpl implements BankService{
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
public boolean transfer(Long fromId,Long toId, double amount) {
return (Boolean)transactionTemplate.execute( new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result=bankDao.transfer(formId,toId,amount);
} catch (Exception e) {
status.setRollbackOnly();
result= false ;
}
return result;
}
});
}
}
|
配置文件也有相应的修改:
<bean id= "bankService"
class = "footmark.spring.core.tx.programmatic.template.BankServiceImpl" >
<property name= "bankDao" ref= "bankDao" />
<property name= "transactionTemplate" ref= "transactionTemplate" />
</bean>
|
声明式事务管理
Spring的声明式事务管理在底层是建立在AOP的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行目标完成之后根据执行情况提交或者回滚事务。
声明式事务最大的优点在于 不需要通过编程的方式管理事务,这样就不需要在业务逻辑中参杂事务管理代码,只需要在配置文件中做相关的事务规则声明,便可将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正式AOP的用武之地。
声明式事务的大致流程如下图:
下面介绍几种常用的声明式事务策略:
基于TransactionInterceptor的声明式事务管理
<!-- 定义事务规则 -->
<bean id= "transactionInterceptor"
class = "org.springframework.transaction.interceptor.TransactionInterceptor" >
<!-- 指定事务管理器 -->
<property name= "transactionManager" ref= "transactionManager" />
<!-- 定义事务规则 -->
<property name= "transactionAttributes" >
<props>
<!-- key 是方法名 ,值为事务属性-->
<prop key= "transfer" >PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 组装target和advice -->
<bean id= "bankService" class = "org.springframework.aop.framework.ProxyFactoryBean" >
<property name= "target" ref= "bankService" />
<property name= "interceptorNames" >
<list>
<idref bean= "transactionInterceptor" />
</list>
</property>
</bean>
|
这里需要说明一下指定事务属性的取值规则:
传播行为[.隔离级别][. 只读属性][.超时属性][不影响提交的异常][.导致回滚的异常]
- 传播行为是唯一必须设置的属性,其他都可以忽略,Spring 为我们提供了合理的默认值.
- 传播行为的取值必须以"PROPAGATION_"开头,具体包括 PROPAGATION_MANDATORY、PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、 PROPAGATION_REQUIRED、ROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取值
- 隔离级别的取值必须以"ISOLATION_"开头,具体包括ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE,共五种取值。
- 如果事务是只读的,那么我们可以指定只读属性,使用readOnly指定,否则我们不需要设置该属性
- 超时属性的取值必须以"TIMEOUT_"开头,后面跟一个int类型的值,表示超时时间,单位是秒。
- 不影响提交的异常是指,即使事务中抛出了这些类型的异常,事务仍然正常提交。必须在每一个异常的名字前面加上"+"。异常的名字可以是类名的一部分。比如"+RuntimeException"、"+tion"等等
- 导致回滚的异常是指,当前事务中抛出这些类型的异常时,事务将回滚。必须在每一个异常的名字前面加上"-"。异常的名字可以是类名的全部或者部分,比如"-RuntimeException"、"-tion"等等.
举两个例子:
<property name= "*Service" >
PROPAGATION_REQUIRED. ISOLATION_READ_COMMITTED. TIMEOUT_20.
+AbcException. +DefException. -HijException
</property>
|
上面的表达式主要针对方法名为Service结尾的方法,使用PROPAGATION_REQUIRED事务传播行为,事务的隔离级别是ISOLATION_READ_COMMITTED, 超时时间为20秒,当事务抛出AbcException或者DefException类型的异常,则仍然提交,当抛出HijException类型的异常时,必须回滚事务。这里没有指定readOnly,表示事务不是只读的。
基于TransactionProxyFactoryBean的事务管理
上面的配置有个局限,即配置文件太多. 我们必须给每一个目标对象配置一个ProxyFactoryBean,加上目标对象本身,每一个业务类可能需要对应三个<bean/>配置,随着业务类的增多,配置文件会变得越来越庞大,所以Spring提供了TransactionProxyFacotyBean,用于将TransactionInterceptor和ProxyFactoryBean的配置合二为一。
<!-- transactionProxyFacotryBean方式 -->
<bean id= "bkService"
class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
<property name= "target" ref= "bankServiceTarget" />
<property name= "transactionManager" ref= "transactionManager" />
<property name= "transactionAttributes" >
<props>
<prop key= "transfer" >PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
|
我们把这种配置方式称为 Spring 经典的声明式事务管理。
基于<tx>命名空间的声明式事务管理
Spring2.x引入了<tx> 命名空间,结合使用<aop>命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。另外,得益于<aop>命名空间的切入点表达式支持,声明式事务变得更加强大.
<!-- tx命名空间方式 -->
<tx:advice id= "bkAdvicetx" transaction-manager= "transactionManager" >
<tx:attributes>
<tx:method name= "transfer" propagation= "REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression= "execution(* *.transfer(..))"
id= "bkPointCut" />
<aop:advisor advice-ref= "bkAdvice" pointcut-ref= "bkPointCut" />
</aop:config>
|
由于使用了切点表达式,我们就不需要针对每一个业务类创建一个代理对象了。
另外,如果配置的事务管理器 Bean 的名字取值为"transactionManager",
则我们可以省略 <tx:advice> 的 transaction-manager 属性,因为该属性的默认值即为"transactionManager"。
基于@Transactional的声明式事务管理
Spring2.x还引入了基于Annotation的方式,具体主要涉及到@Transactional标注。
@Transactional可以作用于接口、接口方法、类以及类方法上。
当作用于类上时,该类的所有 public方法都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
@Transaction (propagation=Propagation.REQUIRED)
public boolean transfer(Long fromId,Long toId, double amount){
return bankDao.transfer(fromId,toId,amount);
}
|
Spring采用BeanPostProcessor来处理Bean中的标注,因此我们需要在配置文件中作如下声明来激活该后处理Bean,
<tx:annotation-driven transaction-manager= "transactionManager" />
|
transaction-manager 属性的默认值是 transactionManager,如果事务管理器 Bean 的名字即为该值,则可以省略该属性。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,否则将被忽略,也不会抛出任何异常。
基于 <tx> 命名空间和基于 @Transactional 的事务声明方式各有优缺点。
基于 <tx> 的方式,其优点是与切点表达式结合,功能强大。利用切点表达式,一个配置可以匹配多个方法,
而基于 @Transactional 的方式必须在每一个需要使用事务的方法或者类上用 @Transactional 标注,尽管可能大多数事务的规则是一致的,但是对 @Transactional 而言,也无法重用,必须逐个指定。
另一方面,基于 @Transactional 的方式使用起来非常简单明了,没有学习成本。开发人员可以根据需要,任选其中一种使用,甚至也可以根据需要混合使用这两种方式。
分享到:
相关推荐
Spring事务管理的目的是确保数据的一致性和完整性,尤其是在多操作、多资源的环境中。本Demo将深入探讨Spring如何实现事务的管理。 首先,Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。 ...
### Spring事务与数据库操作 #### 一、Spring的声明式事务管理 在现代软件开发中,事务处理是非常关键的一部分,特别是在涉及多个数据操作时。Spring框架提供了强大的事务管理能力,可以方便地集成到应用程序中。...
Spring事务管理是Spring框架的核心特性之一,主要用于处理应用程序中的数据一致性问题。在Spring中,事务管理分为编程式和声明式两种方式。本篇文章将详细解释Spring事务管理的流程,以及如何通过时序图来理解这一...
本资源包提供了进行Spring事务管理开发所需的所有关键库,包括框架基础、核心组件、AOP(面向切面编程)支持、日志处理、编译工具以及与数据库交互的相关jar包。下面将对这些知识点进行详细解释: 1. **Spring框架*...
Spring事务原理和配置 Spring事务原理是指Spring框架中的一种机制,用于管理事务,并提供了多种配置方式。事务是指一系列的操作,作为一个整体执行,如果其中某个操作失败,整个事务将回滚。Spring事务原理围绕着两...
本主题将深入探讨“Spring事务案例分析.zip”中的关键知识点,包括Spring事务管理及其在实际项目中的应用。 首先,我们来了解什么是Spring事务管理。在分布式系统或数据库操作中,事务管理是确保数据一致性和完整性...
标题“Spring事务管理失效原因汇总”指出了本文的核心内容是分析在使用Spring框架进行事务管理时可能遇到的问题及其原因。描述部分进一步说明了事务失效的后果往往不明显,容易在测试环节被忽略,但在生产环境中出现...
在Spring框架中,事务管理是核心特性之一,它允许开发者以声明式或编程式的方式处理事务。本示例“spring 事务传播 demo”将聚焦于Spring的事务传播行为,这是在多个方法调用中控制事务边界的关键概念。下面我们将...
这个名为"Spring事务小demo"的项目提供了一个实践示例,帮助开发者了解Spring事务处理的基本概念和用法。 首先,Spring事务管理是Spring框架的核心特性之一,它允许我们以声明式或编程式的方式管理事务。声明式事务...
本篇将深入探讨Spring事务管理的核心概念、工作原理以及如何使用`spring-tx-3.2.0.RELEASE.jar`这个jar包。 首先,我们需要理解什么是事务。在数据库系统中,事务是一组操作,这些操作被视为一个整体,要么全部完成...
本文将深入探讨在Spring框架中如何管理事务,以“Spring 事务简单完整例子”为出发点,结合标签“spring,事务,jdbc事务”,我们将详细解释Spring事务管理的原理和实践。 首先,Spring提供了两种事务管理方式:编程...
Spring事务详细讲解 在 Spring 框架中,事务管理扮演着非常重要的角色。Spring 声明式事务让我们从复杂的事务处理中得到解脱,使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要...
Spring事务机制是Java开发中非常重要的一个概念,它在企业级应用中扮演着核心角色,确保数据的一致性和完整性。Spring提供了多种事务管理方式,包括编程式事务管理和声明式事务管理。在这篇DEMO中,我们将重点探讨...
本DEMO主要探讨的是Spring事务的传播行为和隔离级别,这些概念对于理解和优化数据库操作至关重要。让我们深入理解这些概念及其实际应用。 首先,我们来谈谈事务的传播行为。在Spring中,当一个方法被另一个具有事务...
当我们在使用 Spring 所提供的事务功能时,如果是仅仅处理单个的事务,是比较容易把握事务的提交与回滚,不过一旦引入嵌套事务后,多个事务的回滚和提交就会变得复杂起来,各个事务之间是如何相互影响的,是一个值得...
Spring事务操作示例(四种方式),包含完整代码和数据库文件(基于MySQL,在项目sql文件夹中),可运行,学习Spring事务详见博客:http://blog.csdn.net/daijin888888/article/details/51822257
本篇将基于"Spring事务传播Demo"来深入探讨Spring事务管理和传播行为。 首先,我们需要理解什么是事务。在数据库操作中,事务是一组操作,这些操作要么全部执行,要么全部不执行,以确保数据的一致性和完整性。在...
Spring事务管理是Spring框架的核心特性之一,主要用于处理应用程序中的数据一致性问题。在多线程、分布式系统中,事务管理显得尤为重要。本节将详细介绍Spring如何通过XML配置和注解方式来实现事务管理。 首先,...