spring的事物源码分析和使用详解

目录

注意事项(以下注意事项在demo中都有体现)

一、Spring中事物管理源码分析

spring事物中几个关键的类或者接口:

1、PlatformTransactionManager 事务管理器,包含三个主要的方法,如下图:

2、TransactionDefiition 定义事务的类型,包含事物的一些属性。

3、TransactionStatus 代表一个事务运行的状态

spring事物的入口点

事物的创建

事物提交

事物回滚

二、spring事物常用属性

事物传播属性解释:

不同的事物传播属性在嵌套事物中的表现:

三、事物隔离级别

demo传送门


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

注意事项(以下注意事项在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

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(常用) -- 新建事务,如果当前存在事务,把当前事务挂起。 举一个应用场景:现在有一个发送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传送门

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值