spring的事物管理详解
注意事项(以**意事项在demo中都有体现)
1、@Transactional事务只有配置在public方法上,且是被外部调用时才有效。
2、注意设置rollbackFor属性,只有RuntimeExcetion会触发回滚,可以设置rollbackFor = {Exception.class}使所有异常都可以回滚,结合具体业务具体处理,可能有的业务抛出的某些异常并不需要触发回滚,所以此时应该细化处理异常。
3、不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
4、将@Transactional放置在类级的声明中,会使得所有方法都有事务。最好把@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
一、Spring中事物管理源码分析
注:在查看源码前建议了解一下spring的五种通知类型。
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方法中,如下:
事物提交的代码在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 -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
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)是不会产生新的事务,他们会在同一个事物中执行。