spring事务管理
使用方式分类:
- 声明式事务 @Transactional 通过AOP来实现的;起到事务管理的作用,同时不影响业务代码的具体实现
- 编程式事务 当遇到动态切换数据源的问题或用户在代码中精确定义事务的边界,需要写AOP拦截,编程式事务管理
怎么实现的?是使用PlatformTransactionManager代理还是sqlsession?
spring只是提供一个事务平台管理,具体事务管理由各个平台自己控制;spring通过org.springframework.transaction.PlatformTransactionManager为每个平台提供对应的事务管理器;
spring支持的平台:
- JDBC平台
本地事务,支持单个数据源的事务管理
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 4. 事务管理 : DataSourceTransactionManager dataSource:引用上面定义的数据源 -->
<tx:annotation-driven transaction-manager="txManager" order="1" />
DataSourceTransactionManager通过调用JDBC的java.sql.Connection来管理事务;对于使用持久化框架如mybatis参与的,mybatis本身也不管理,而是将事务管理交由容器(详情见Mybatis章)
- Hibernate或者JPA
- 原生API事务
全局事务,支持多数据源的分布式事务
包括面向开发人员的Java 事务编程接口(JTA:Java Transaction API)和 面向服务提供商的Java 事务服务 (JTS;Java Transaction Service);
JTA组件包括一个事务管理器(Transaction Manager)和多个支持 XA 协议的资源管理器 ( Resource Manager );事务管理器显式控制异构数据源的执行情况,资源管理器则根据约定的接口协调者两种事务资源从而实现分布式事务;
与本地事务相比,XA 协议的系统开销大,在系统开发过程中应慎重考虑是否确实需要分布式事务;
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚
使用方式
- 全注解+@Transactional注解
- tx标签配置+AOP拦截器
- 为每个Bean配置一个代理
注意点
- Spring框架下,所有SQL异常(均是checked异常)都被org.springframework重写为RuntimeException,事务因此也会发生回滚
- 有时对于checked异常,影响后续如调接口等操作的,需要进行针对checked异常做sql的回滚时,
- 抛出RuntimeException异常;
- 修改Transactional的rollbackFor属性
@Transactional //默认只有unchecked的运行时异常回滚->@Transactional(rollbackFor=Exception.class) //修改成所有异常回滚
- 在service层使用
- 事务控制到子函数,涉及事务传播问题
- 具体使用时,跨Service的方法才生效
- 跨Service调用方法时,都会经过org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor.intercept()方法,只有经过此处,才能对事务进行控制
- 多数据源的事务控制,使用JtaTransactionManager
- 测试springmvc transactional事务使用问题
09:06:20.584 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c6742d0]
09:06:20.584 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c6742d0] from current transaction
09:06:20.794 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization rolling back SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c6742d0]
09:06:20.794 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c6742d0]
- JUint测试时控制回滚
事务属性
事务传播行为:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
- PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务(默认)
//事务属性 PROPAGATION_REQUIRED
methodA{
……
methodB();
……
}
//事务属性 PROPAGATION_REQUIRED
methodB{
……
}
methodB会加入到methodA的事务中一起,影响等同
- PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行(依赖父事务,没有则普通执行)
//事务属性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事务属性 PROPAGATION_SUPPORTS
methodB(){
……
}
单独调用methodB时不建立事务,只有被required事务调用时有效
- PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常(必须依赖父事务)
//事务属性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事务属性 PROPAGATION_MANDATORY
methodB(){
……
}
methodB必须被调用,参与事务
- PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起;需要使用 JtaTransactionManager作为事务管理器(不依赖父事务,可独立运行)
//事务属性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事务属性 PROPAGATION_REQUIRES_NEW
methodB(){
……
}
methodB作为内层事务独立运行,不受外层事务methodA的执行成功与否影响
- PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务(忽略父事务)
- PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常(排斥父事务)
- PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中;如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。(父事务影响自己,但自己不影响父事务)
//事务属性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事务属性 PROPAGATION_NESTED
methodB(){
……
}
内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作;而内层事务操作失败并不会引起外层事务的回滚。
隔离级别:
并发事务引起的问题:
脏读(Dirty reads) 一个事务读到另一个未提交事务(回滚了)的数据
不可重复读(Nonrepeatable read) 在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新
幻读(Phantom read) 一个事务读取多行时,另一个事务插入/删除几条,导致读取的多了
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别(默认)
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
隔离级别越高,消耗的系统验证越多
只读属性:
如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施
默认非只读
事务超时:
默认不限
回滚规则:
spring默认遇到runtimeException时回滚,但你也可以控制checked异常回滚或者某些unchecked不回滚
实验
- 内层事务不受外层事务影响主要使用 PROPAGATION_NOT_SUPPORTED
- 同一个service内,外层事务管理,忽略内部调用方法的 PROPAGATION属性,一切数据库操作都会走事务;要想生效,必须单独放置一个service类中