事务与spring事务

事务

事务的特性(ACID)

原子性(Atomicity):

标识将事务中所有的操作进行捆绑层一个不可分割的单元格,
计对事务所有进行的数据库修改等操作,要么全部执行,要么就是全部失败
隔离性(Isolation):
指的是一个事务的执行,不能被其他的事务进行干扰,一个事务内部的操作以及使用的数据库对并发的其他事务都是进行隔离的,并发执行的各个事务之间是不能进行相互干扰的  事务之间隔离,互不干扰
持久性(Durability):
永久性,指一个事务提交之后,它对数据库中数据的改变应该是永久性

一致性(Consistency):
事务前后,数据库数据完整性不被破坏,总是与预期一致的状态,就像借钱,一方借多少,另一方加多少,数据不会凭空增多,也不会凭空减少。

 

数据是一致且持久的,操作是原子且隔离的

 

 

 事务的四种隔离级别

 

 

①read uncommitted(读未提交):事物的修改,即使没有提交,对其他事务也是可见的。事务读未提交的数据成为脏读。
②read committed(读已提交):只能读取到其他事务已经提交的数据。
③repeatable read(可重复读):行锁锁住读取的数据,不允许修改,该级别解决了脏读、不可重复读的问题,保证了在一个事物中多次读到的数据是一致的,但是不影响其他事务insert,所以没有解决幻读的问题。(默认)

事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。实现原理(MVCC [ 多版本并发控制 ])

有个版本的概念,不会读到事务开启之后其他事务对该数据修改后的视图

对于可重复读,查询只承认在事务启动前就已经提交完成的数据;

对于读提交,查询只承认在语句启动前就已经提交完成的数据

https://www.jianshu.com/p/8fd9585810a2


④serializable(串行化):事务挨个执行,避免了幻读的问题。

 

数据库的事务隔离越严格并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的

同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。

主流数据库默认的事务隔离级别

Mysql——repeatable read(可重复读)

Oracle——read committed(读已提交)

PostGreSql——read committed(读已提交)

事务并发可能产生的问题(不考虑隔离级别)

脏读

读到另一事务还未提交的数据。

(只有读未提交隔离级别才会出现这种问题 。 eg:A对数据减10但还未提交,B读取到的数据数据已经少了10)

不可重复度

一个事务多次对相同的数据读取可能会出现不同的结果

读未提交和读已提交的隔离级别下都可能会出现不可重复读的问题,关键在于重复读取的数据同时在被其他事务进行修改操作)

幻读/虚读:

当前事务读取数据时,读范围内的数据,被其他事务穿插有修改操作,导致出现读取幻象。

(在读未提交、读已提交和可重复读三种事务隔离级别下都可能会出现)

 

Spring事务

Spring管理事务的方式

  • 编程式事务,在代码中硬编码。(不推荐使⽤)
  • 声明式事务,在配置⽂件中配置(推荐使⽤)
    •   基于XML的声明式事务
    •   基于注解的声明式事务

 

Spring事务五种隔离级别

一种控制并发执行的事务对数据操作的规则。

TransactionDefinition 接⼝中定义了五个表示隔离级别的常量

 

TransactionDefinition.ISOLATION_DEFAULT数据库默认

使⽤后端数据库默认的隔离级别,Mysql 默认采⽤的 REPEATABLE_READ隔离级别 Oracle 默认采⽤的 READ_COMMITTED隔离级别.

TransactionDefinition.ISOLATION_READ_UNCOMMITTED 读未提交

最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读  

TransactionDefinition.ISOLATION_READ_COMMITTED 读已提交

允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣

TransactionDefinition.ISOLATION_REPEATABLE_READ: 可重复读

对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。

TransactionDefinition.ISOLATION_SERIALIZABLE: 序列化

最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该

级别可以防⽌脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会⽤到该级别。

                                              --参考java Guide文章

 

Spring事务的7种传播行为

⽀持当前事务的情况
TransactionDefinition.PROPAGATION_REQUIRED 存在则加入,没有就新建

如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。

TransactionDefinition.PROPAGATION_SUPPORTS存在则加入,没有则不管

如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。

TransactionDefinition.PROPAGATION_MANDATORY存在则加入,没有就抛异常

如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不⽀持当前事务的情况
TransactionDefinition.PROPAGATION_REQUIRES_NEW创建新事物,存在则挂起

创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED非事务运行,存在则挂起

以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER非事务运行,存在则抛异常

以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。

其他情况:
TransactionDefinition.PROPAGATION_NESTED存在则嵌套事务,没有就新建

如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价TransactionDefinition.PROPAGATION_REQUIRED。

                                            --参考java Guide文章

 

Spring事务回滚rollbackFor

@Transactional(rollbackFor = Exception.class)

当 @Transactional 注解作⽤于类上时,该类的所有 public ⽅法将都具有该类型的事务属性,同时,我们也可以在⽅法级别使⽤该标注来覆盖类级别的定义。

一般用于方法级别。

如果类或者⽅法加了这个注解,那么这个类⾥⾯的⽅法抛出异常,就会回滚,数据库⾥⾯的数据也会回滚。

在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException时候才会回滚,加上 rollbackFor=Exception.class ,可以让事物在遇到⾮运⾏时异常时也回滚。

 

 

场景验证

参考: https://blog.csdn.net/u014532775/article/details/106690766

Propagation.REQUIRED

外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰
外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务只要一个方法回滚,整个事务均回滚。
特别:对于下面这种捕获异常的,事务注解的方法已经抛出异常了,只是在方法外进行了捕获并不影响事务回滚 =》三个事务都会回滚
 @Transactional
    @Override
    public void transaction_required_required_exception_try(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);
 
        User2 user2=new User2();
        user2.setName("李四");
        try {
            user2Service.addRequiredException(user2);
        } catch (Exception e) {
            System.out.println("方法回滚");
        }
    }
    
    
    
@Service
public class User1ServiceImpl implements User1Service {
    //省略其他...
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(User1 user){
        user1Mapper.insert(user);
    }
}


@Service
public class User2ServiceImpl implements User2Service {
    //省略其他...
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(User2 user){
        user2Mapper.insert(user);
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequiredException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }
 }

 

PROPAGATION_REQUIRES_NEW

外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰

外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

Propagation.REQUIRES_NEW修饰方法抛出的异常在外部方法中如不捕获,会影响外部方法事务的回滚。

 

Spring事务方法套方法,内部事务方法不生效?

Spring事务用到代理,代理去对标记有@Transactional的方法开启事务startTransaction(),内部的调用是由被代理对象直接调用,不会再开启事务,因此内部事务不生效

解决办法,通常是将方法分别放在不同的service中

posted on 2023-02-24 00:23  or追梦者  阅读(16)  评论(0编辑  收藏  举报