Spring中的事务总结
-
事务介绍
- 事务具备4个特性:ACID ,详见 db_index.md
- 在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作
-
Spring 支持的事务策略,包括两种
-
全局事务: 由应用服务器管理,需要Web服务器支持 JTA(java transaction api)技术。全局事务可以跨越多个事务性资源(支持跨越多个数据库资源)
<!-- 配置Jta 全局事务管理--> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> <!-- 注意:无论使用 hibernate, mybatis还是JDBC, JTA 配置都一样 --> <!-- 当然,使用JTA全局事务时还需要Web服务器的支持,不同应用服务器所提供的JTA全局事务也存在细节上的差异,因此实际配置时可能会用到JtaTransactionManager的子类,如: OC4JtaTransactionManager(Oracle提供的Web服务器),WebLogicJtaTransactionManager (Bee 提供的Web服务器) 等 -->
-
局部事务:和采用的持久化技术有关,当使用 JDBC 时,需要使用connection对象来操作事务;当使用Hibernate时,需要使用Session对象来操作事务;当使用MyBatis时,和JDBC配置事务方式一样;
<!-- 配置JDBC数据源的局部事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <!-- 配置JPA 数据源的局部事务管理 --> <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <!--使用Spring容器中已有的DataSource,这样就不需要使用persistence.xml文件中的数据源了--> <property name="dataSource" ref="dataSource" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.JpaTransactionManager"> <constructor-arg ref="emf" /> </bean> <!-- 配置Hibernate的局部事务管理,需要使用如下配置 --> <!-- 要想在Spring中使用 Hibernate,需要配置 sessionFactory 类 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mappingresource" /> <property name="hibernateProperties" /> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置MyBatis的局部事务管理 --> <!-- 要想在Spring中使用 MyBatis,需要配置 sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务部分,仍然使用的是JDBC数据源的局部事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean>
-
-
Spring Boot 的事务原理
- Spring的事务是通过PlatformTransactionManager 接口实现的,不同的持久化技术,这个接口会有不同的实现类。如果添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
- 通过Spring容器(IOC)技术,可以轻松注入 JTA事务或非JTA事务。应用程序不与任何实现类(具体的事务)耦合, 应用程序实际上是面向 PlatformTransactionManager 接口编程的,因此可以将应用程序和持久化技术、事务彻底解耦
- TransactionDefinition :该接口包含与事务属性有关的方法,事务隔离级别和事务传播行为
-
TransactionDefinition.ISOLATION_READ_COMMITTED:隔离级别是指若干个并发的事务之间的隔离程度。隔离级别有4种 :
1) read-uncommitted : 所有事务都可以看到其他未提交事务的执行结果
2)READ_COMMITTED:表示一个事务只会读取已经提交的数据,该级别可以防止脏读,这也是大多数情况下的默认值。它也是 SQLServer,Oracle的默认级别
3)REPEATABLE READ(可重复读):意思是在一个事务中第一次读和第二次读数据是一致的,不受其他事务的影响。该级别可以解决不可重复读的问题,它是MySQL的默认级别。
4)串行化:解决幻读的问题
-
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
-
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。
-
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。
-
事务的回滚规则:我们可以控制事务在抛出某些未检查异常时仍然提交事务,或者在抛出某些已检查异常时回滚事务
-
-
两种事务管理方式: 全面分析Spring的编程式事务和声明式事务
-
编程式事务管理:应用程序可以直接获取transactionManager bean,该bean 是PlatformTransactionManager接口的实例,所以可以通过该接口提供的3个方法,开始事务处理
-
基于 TransactionDefinition、PlatformTransactionManager、TransactionStatus 编程式事务管理是 Spring 提供的最原始的方式,通常我们不会这么写
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionDefinition txDefinition; private PlatformTransactionManager txManager; ...... public boolean transfer(Long fromId, Long toId, double amount) { // PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务) 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); System.out.println("Transfer Error!"); } 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>
-
基于 TransactionTemplate 的编程式事务管理是对上一种方式的封装,使得编码更简单、清晰
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer(final Long fromId, final Long toId, final double amount) { return (Boolean) transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false; System.out.println("Transfer Error!"); } return result; } }); } }
<bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>
-
-
声明式事务管理:不用在应用程序中编写任何事务代码,而是在XML文件中配置事务,推荐使用
-
基于 TransactionInterceptor 的声明式事务是 Spring 声明式事务的基础,通常也不建议使用这种方式。可以使用 BeanNameAutoProxyCreator 完成自动事务代理
<beans...> ...... <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <!--transactionAttributes 定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性--> <property name="transactionAttributes"> <props> <!--事务属性的规则 : 传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]--> <!-- 例如: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,TIMEOUT_20,+AbcException,+DefException,-HijException --> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="bankServiceTarget"/> <property name="interceptorNames"> <list> <idref bean="transactionInterceptor"/> </list> </property> </bean> ...... </beans>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*ServiceImpl</value> </list> </property> <property name="interceptorNames"> <list> <!-- 下面定义BeanNameAutoProxyCreator所需的事务拦截器 --> <value> transactionInterceptor </value> </list> </property> </bean>
-
基于 TransactionProxyFactoryBean 的声明式事务是上种方式的改进版本,简化的配置文件的书写,这是 Spring 早期推荐的声明式事务管理方式
- 声明式事务使用 TransactionProxyFactoryBean ,为目标Bean生成一个事务代理bean
- 基于 Spring AOP技术,这个事务代理bean会改写目标bean的方法,织入开始事务和结束事务的增强处理
- 使用 XML Schema 可以简化 TransactionProxyFactoryBean 事务代理相关的配置,具体看书上
<beans......> ...... <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" 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> ...... </beans>
-
基于 和 命名空间的声明式事务管理是目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。
<beans......> ...... <bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <tx:advice id="bankAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/> <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/> </aop:config> ...... </beans>
-
基于 @Transactional 的方式将声明式事务管理简化到了极致。透彻的掌握 Spring 中的 @Transactional ; @EnableTransactionManagement 的使用。Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。
//启注解事务管理,等同于xml配置方式的 <tx:annotation-driven /> @EnableTransactionManagement @SpringBootApplication public class ProfiledemoApplication { @Bean public Object testBean(PlatformTransactionManager platformTransactionManager){ System.out.println(">>>" + platformTransactionManager.getClass().getName()); return new Object(); } public static void main(String[] args) { SpringApplication.run(ProfiledemoApplication.class, args); } }
// Service层 @Transactional(propagation = Propagation.REQUIRED,readOnly=true) public boolean transfer(Long fromId, Long toId, double amount) { return bankDao.transfer(fromId, toId, amount); }
-
@Transactional 注解的属性说明
属性名 说明 name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 propagation 事务的传播行为,默认值为 REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建新的事务运行。PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。 isolation 事务的隔离度,默认值采用 DEFAULT。 timeout 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 rollback-for 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不会回滚事务。如果在事务中抛出其他类型的异常,并期望 Spring 能够回滚事务,可以指定 rollbackFor no-rollback- for 抛出 no-rollback-for 指定的异常类型,不回滚事务。
-
-
-
常见问题
-
- @Transactional 加载在private方法上是无效的,只有@Transactional 注解应用到 public 方法,才能进行事务管理。
- 不过当private方法被加了@Transactional注解的public方法调用时,同样会被事务管理,即使这个private方面没有添加@Transactional注解
-
JPA 抛出异常: Transaction rolled back because it has been marked as rollback-only"
- 原因是:一个事务方法A调用另一个事务方法B时,B如果报错事务就已经是回滚状态了,放回到A之后,A方法继续执行,提交了事务(已经是回滚状态的事务),就报错了 Transaction marked as rollback only
-
同一类中的方法自调用问题
- 如果同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,出现异常不会发生回滚。
-
在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作
-