目录
1、PlatformTransactionManager 事务管理器,包含三个主要的方法,如下图:
2、TransactionDefiition 定义事务的类型,包含事物的一些属性。
3、TransactionStatus 代表一个事务运行的状态
注意事项(以下注意事项在demo中都有体现)
1、@Transactional事务只有配置在public方法上,且是被外部调用时才有效(内部调用的话,调用的方法也要加上@Transactional)
@Transactional
public int insert(User user){
int i = userDao.insert(user);
return i;
}
/**
* 内部调用时,下面情况会回滚,如果去掉@Transactional注解,不会回滚
* @param user
* @return
*/
@Transactional
public int insert2(User user){
int i = this.insert(user);
try {
int a = 1/0;
} catch (Exception e) {
throw new RuntimeException(e);
}
return i;
}
此处的外部调用指的是:
@RequestMapping("/insert")
public int insert(){
User user = new User();
user.setUserName("包华杰");
user.setPassword("123");
user.setPhone("234124");
return userService.insert2(user);
}
2、注意设置rollbackFor属性,只有RuntimeExcetion会触发回滚,可以设置rollbackFor = {Exception.class}使所有异常都可以回滚,结合具体业务具体处理,可能有的业务抛出的某些异常并不需要触发回滚,所以此时应该细化处理异常。
3、不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
4、将@Transactional放置在类级的声明中,会使得所有方法都有事务。最好把@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
一、Spring中事物管理源码分析
注:在查看源码前建议了解一下spring的五种通知类型。
spring事物中几个关键的类或者接口:
1、PlatformTransactionManager 事务管理器,包含三个主要的方法,如下图:
spring中事物管理器的实现方式有以下几种(因为有部分jar包依赖没导入,么有显示全):
- DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;
- JdoTransactionManager:位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理;
- JpaTransactionManager:位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理;
- HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
- JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
- OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
- WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
- WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。
2、TransactionDefiition 定义事务的类型,包含事物的一些属性。
3、TransactionStatus 代表一个事务运行的状态
spring事物的入口点
org.springframework.transaction.interceptor.TransactionInterceptor类的invoke方法,如下:
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
事物的创建
代码在org.springframework.transaction.interceptor.TransactionAspectSupport中的invokeWithinTransaction方法中,如下,依次点进去会发现事物真正开始的地方是status = tm.getTransaction(txAttr); getTransaction方法中进行事物传播属性的判断,根据不同的属性执行不同的事物创建,最后执行doBegin(transaction, definition);方法,在doBegin方法中执行con.setAutoCommit(false);将事物设置成非自动提交。
事物提交
代码在org.springframework.jdbc.datasource.DataSourceTransactionManager中的doCommit方法中,如下:
事物回滚
代码在org.springframework.jdbc.datasource.DataSourceTransactionManager中的doRollback方法中,如下:
以上的提交、回滚会根据不同的事物管理器执行不同的类。这里使用的是org.springframework.jdbc.datasource.DataSourceTransactionManager事物管理器。
TransactionAspectSupport类是比较关键的一个类,该类的invokeWithinTransaction方法会判断目标方法是不是有事物,如果有事物创建事物、执行事物、完成事,一条龙服务。
二、spring事物常用属性
The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, "start a brand new read-only transaction when this method is invoked, suspending any existing transaction". The default @Transactional settings are as follows:
- Propagation setting is PROPAGATION_REQUIRED.
- Isolation level is ISOLATION_DEFAULT.
- Transaction is read/write.
- Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
- Any RuntimeException triggers rollback, and any checked Exception does not.
Property | Type | Description |
String | Optional qualifier specifying the transaction manager to be used. | |
enum: Propagation | Optional propagation setting. | |
isolation | enum: Isolation | Optional isolation level. Only applicable to propagation REQUIRED or REQUIRES_NEW. |
timeout | int (in seconds granularity) | Optional transaction timeout. Only applicable to propagation REQUIRED or REQUIRES_NEW. |
readOnly | boolean | Read/write vs. read-only transaction. Only applicable to REQUIRED or REQUIRES_NEW. |
rollbackFor | Array of Class objects, which must be derived from Throwable. | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | Array of class names. Classes must be derived from Throwable. | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor | Array of Class objects, which must be derived from Throwable. | Optional array of exception classes that must not cause rollback. |
noRollbackForClassName | Array of String class names, which must be derived from Throwable. | Optional array of names of exception classes that must not cause rollback. |
事物传播属性解释:
PROPAGATION_REQUIRED (常用)-- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,默认的
被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW(常用) -- 新建事务,如果当前存在事务,把当前事务挂起。 举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED(常用) -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚, 因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理
支持当前事务,新增Savepoint点,与当前事务同步提交或回滚,嵌套事务一个非常重要的概念就是内层事务依赖于外层事务,外层事务失败时,会回滚内层事务所做的动作,而内层事务操作失败并不会引起外层事务的回滚。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
它们非常 类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。 使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚, 因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理,它是一个真正的嵌套事务。
事物嵌套(注意理解PROPAGATION_REQUIRED 、PROPAGATION_NESTED、PROPAGATION_REQUIRES_NEW的区别):例如,serviceA和ServiceB的所有方法都被配置声明事务,当serviceA的方法调用ServiceB的方法,大部分人认为methodA和methodB都各自有自己的事务,这会带来两个问题:1)性能下降;2)methodB事务提交后,methodA因异常而回滚,methodB去无法回滚。
/**
* 事物嵌套:userServiceC.insertNew事物属性为REQUIRES_NEW,其他事物属性都是PROPAGATION_REQUIRED,userServiceB.insert事物抛出异常后,userServiceC.insertNew事物不会回滚
* @param user
* @return
*/
@Transactional
public int insert4(User user){
int i = 0;
int j = 0;
int x = 0;
try {
x = userDao.insert(user);
j = userServiceC.insertNew(user);
i = userServiceB.insert(user);
} catch (Exception e) {
throw new RuntimeException();
}
return i+j;
}
将上述的事务传播属性都配置为PROPAGATION_REQUIRED,问题得以解决,因为当userServiceC.insertNew(user)执行的时候,当前线程已经开始了一个事务,所以userServiceC.insertNew(user)是不会产生新的事务,他们会在同一个事物中执行。
不同的事物传播属性在嵌套事物中的表现:
外层事务 Service A 的 Method A() 调用 内层Service B 的 Method B()
1、PROPAGATION_REQUIRED(spring 默认)
ServiceB.methodB() 和ServiceA.methodA()事物传播属性 都定义成PROPAGATION_REQUIRED,执行 ServiceA.methodA() 的时候spring已经起了事务,这时调用 ServiceB.methodB(),ServiceB.methodB() 看到自己已经运行在 ServiceA.methodA() 的事务内部,就不再起新的事务,如果 ServiceB.methodB() 运行的时候发现自己没有在事务中,就会开启一个新的事物,这样在ServiceA.methodA() 或者在 ServiceB.methodB() 内的任何地方出现异常,事务都会被回滚。
2、PROPAGATION_REQUIRES_NEW
ServiceA.methodA() 的事务级别为 PROPAGATION_REQUIRED,ServiceB.methodB() 的事务级别为 PROPAGATION_REQUIRES_NEW。执行到 ServiceB.methodB() 的时候,ServiceA.methodA() 所在的事务就会挂起,ServiceB.methodB() 会起一个新的事务,等待 ServiceB.methodB() 的事务完成以后,它才继续执行。这样当ServiceB.methodB()执行完后,ServiceB.methodB()事物进行提交,这个时候ServiceA.methodA() 抛出异常,ServiceA.methodA() 事物回滚但是ServiceB.methodB()事物不会回滚。
3、PROPAGATION_SUPPORTS
ServiceA.methodA() 的事务级别为 PROPAGATION_REQUIRED,ServiceB.methodB() 的事务级别为 PROPAGATION_SUPPORTS,执行到ServiceB.methodB()时,如果发现ServiceA.methodA()已经开启了一个事务,则加入当前的事务,如果发现ServiceA.methodA()没有开启事务,则自己也不开启事务。这种时候,内部方法的事务性完全依赖于最外层的事务。
4、PROPAGATION_NESTED
ServiceB.methodB() 的事务属性被配置为 PROPAGATION_NESTED, 外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚,ServiceB.methodB 如果 rollback, 那么内部事务将回滚到它执行前的 SavePoint 而外部事务可以有以下两种处理方式:
a、捕获异常,执行异常分支逻辑
void methodA() {
try {
ServiceB.methodB();
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点。
b、 外部事务回滚/提交 代码不做任何修改, 那么如果内部事务(ServiceB.methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 ServiceA.methodA) 将根据具体的配置决定自己是 commit 还是 rollback
三、事物隔离级别
参考我的另一篇博客:https://blog.csdn.net/u013268035/article/details/86011806
demo传送门