Spring事务

Spring事务


四个属性

原子性(Atomicity):事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行
一致性(Consistency):当事务完成时,数据必须处于一致状态
隔离性(Isolation):对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务
永久性(Durability):事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性

事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

  1. PROPAGATION_REQUIRED
    说明: 如果当前已经存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值
@Transactional
public void service(){
    serviceA();
    serviceB();
}

@Transactional
serviceA();
@Transactional
serviceB();

serviceA 和 serviceB 都声明了事务,默认情况下,propagation=PROPAGATION_REQUIRED,整个service调用过程中,只存在一个共享的事务,当有任何异常发生的时候,所有操作回滚。

2、PROPAGATION_SUPPORTS

说明:如果当前已经存在事务,那么加入该事务,否则创建一个所谓的空事务(可以认为无事务执行)。

看一个小例子,代码如下:

public void service(){
     serviceA();
     throw new RunTimeException();
}

@Transactional(propagation=Propagation.SUPPORTS)
serviceA();

serviceA执行时当前没有事务,所以service中抛出的异常不会导致 serviceA回滚。

再看一个小例子,代码如下:

public void service(){
     serviceA();
}

@Transactional(propagation=Propagation.SUPPORTS)
serviceA(){
    do sql 1
    1/0;
    do sql 2
}

由于serviceA运行时没有事务,这时候,如果底层数据源defaultAutoCommit=true,那么sql1是生效的,如果defaultAutoCommit=false,那么sql1无效,如果service有@Transactional标签,serviceA共用service的事务(不再依赖defaultAutoCommit),此时,serviceA全部被回滚。

3、 PROPAGATION_MANDATORY

说明:当前必须存在一个事务,否则抛出异常。

看一个小例子,代码如下:

public void service(){
     serviceB();
     serviceA();
}
serviceB(){
    do sql
}
@Transactional(propagation=Propagation.MANDATORY)
serviceA(){
    do sql
}

这种情况执行 service会抛出异常,如果defaultAutoCommit=true,则serviceB是不会回滚的,defaultAutoCommit=false,则serviceB执行无效。

4、PROPAGATN_REQUIRES_NEW

说明:如果当前存在事务,先把当前事务相关内容封装到一个实体,然后重新创建一个新事务,接受这个实体为参数,用于事务的恢复。更直白的说法就是暂停当前事务(当前无事务则不需要),创建一个新事务。 针对这种情况,两个事务没有依赖关系,可以实现新事务回滚了,但外部事务继续执行。

看一个小例子,代码如下:

@Transactional
public void service(){
    serviceB();
    try{
        serviceA();
    }catch(Exception e){
    }
}
serviceB(){
    do sql
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
serviceA(){
    do sql 1
    1/0;
    do sql 2
}

当调用service接口时,由于serviceA使用的是REQUIRES_NEW,它会创建一个新的事务,但由于serviceA抛出了运行时异常,导致serviceA整个被回滚了,而在service方法中,捕获了异常,所以serviceB是正常提交的。 注意,service中的try … catch 代码是必须的,否则service也会抛出异常,导致serviceB也被回滚。

5、Propagation.NOT_SUPPORTED

说明:如果当前存在事务,挂起当前事务,然后新的方法在没有事务的环境中执行,没有spring事务的环境下,sql的提交完全依赖于 defaultAutoCommit属性值 。

看一个小例子,代码如下:

@Transactional
public void service(){
     serviceB();
     serviceA();
}
serviceB(){
    do sql
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
serviceA(){
    do sql 1
    1/0;
    do sql 2
}

当调用service方法的时候,执行到serviceA方法中的1/0代码时,抛出了异常,由于serviceA处于无事务环境下,所以 sql1是否生效取决于defaultAutoCommit的值,当defaultAutoCommit=true时,sql1是生效的,但是service由于抛出了异常,所以serviceB会被回滚。

6、 PROPAGATION_NEVER

说明: 如果当前存在事务,则抛出异常,否则在无事务环境上执行代码。

看一个小例子,代码如下:

public void service(){
    serviceB();
    serviceA();
}
serviceB(){
    do sql
}
@Transactional(propagation=Propagation.NEVER)
serviceA(){
    do sql 1
    1/0;
    do sql 2
}

上面的示例调用service后,若defaultAutoCommit=true,则serviceB方法及serviceA中的sql1都会生效。

7、 PROPAGATION_NESTED

说明: 如果当前存在事务,则使用 SavePoint 技术把当前事务状态进行保存,然后底层共用一个连接,当NESTED内部出错的时候,自行回滚到 SavePoint这个状态,只要外部捕获到了异常,就可以继续进行外部的事务提交,而不会受到内嵌业务的干扰,但是,如果外部事务抛出了异常,整个大事务都会回滚。

注意: spring配置事务管理器要主动指定 nestedTransactionAllowed=true,如下所示:

    <bean id="dataTransactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataDataSource" />
        <property name="nestedTransactionAllowed" value="true" />
    </bean>

看一个小例子,代码如下:

@Transactional
public void service(){
    serviceA();
    try{
        serviceB();
    }catch(Exception e){
    }
}
serviceA(){
    do sql
}
@Transactional(propagation=Propagation.NESTED)
serviceB(){
    do sql1
    1/0;
    do sql2
}

serviceB是一个内嵌的业务,内部抛出了运行时异常,所以serviceB整个被回滚了,由于service捕获了异常,所以serviceA是可以正常提交的。

再来看一个例子,代码如下:

@Transactional
public void service(){
     serviceA();
     serviceB();
     1/0;
}
@Transactional(propagation=Propagation.NESTED)
serviceA(){
    do sql
}
serviceB(){
    do sql
}

由于service抛出了异常,所以会导致整个service方法被回滚。(这就是跟PROPAGATION_REQUIRES_NEW不一样的地方了,NESTED方式下的内嵌业务会受到外部事务的异常而回滚。)


参考

spring事务-说说Propagation及其实现原理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值