spring的事物管理详解

spring事物官网:https://docs.spring.io/spring/docs/5.0.11.RELEASE/spring-framework-reference/data-access.html#appendix

注意事项(以**意事项在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方法中,如下:

 

spring的事物管理详解

 

事物提交的代码在org.springframework.jdbc.datasource.DataSourceTransactionManager中的doCommit方法中,如下:

 

spring的事物管理详解

事物回滚的代码在org.springframework.jdbc.datasource.DataSourceTransactionManager中的doRollback方法中,如下:

 

spring的事物管理详解

以上的提交、回滚会根据不同的事物管理器执行不同的类。这里使用的是org.springframework.jdbc.datasource.DataSourceTransactionManager事物管理器。

 

spring的事物管理详解

TransactionAspectSupport类是比较关键的一个类,该类的invokeWithinTransaction方法会判断目标方法是不是有事物,如果有事物创建事物、执行事物、完成事,一条龙服务。

 

spring的事物管理详解

二、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

value

String

Optional qualifier specifying the transaction manager to be used.

propagation

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)是不会产生新的事务,他们会在同一个事物中执行。

demo传送门