通俗易懂地理解数据库事务与Spring事务的使用(@Transactional)以及两者事务的区别,还有事务的挂起

目录

数据库事务?

ACID?

Java 事务(@Transactional)?

 数据库中的的事务和java中事务的区别?

以下是@Transactional的具体使用方法:

rollBackFor属性

@Transactional不同的含义

一个问题

propagation属性

传播行为NOT_SUPPORTED

事务的挂起

挂起与代码的暂停有什么不同?


数据库事务?

数据库事务是一组数据库操作的集合,这些操作要么全部成功(提交),要么全部失败(回滚)。数据库事务遵循 ACID 原则(原子性、一致性、隔离性和持久性),以确保数据的完整性和可靠性。

示例: 

BEGIN TRANSACTION;

-- 更新客户的地址
UPDATE customers SET address = '新地址' WHERE customer_id = 1;
-- 其它一坨DML操作

-- 检查是否有错误
IF @@ERROR <> 0
BEGIN
    ROLLBACK; -- 如果更新失败,回滚事务
    PRINT '更新失败,事务已回滚';
END
ELSE
BEGIN
    COMMIT; -- 如果更新成功,提交事务
    PRINT '更新成功,事务已提交';
END

ACID?

特性定义关注点示例
原子性事务中的所有操作要么全部成功,要么全部失败。

事务的

完整性

银行转账操作中的扣款和存款要么都成功,要么都失败。
一致性事务执行前后,数据库必须保持一致的状态。

数据库的

完整性约束

账户的总金额在转账前后必须相等。
隔离性事务之间应相互独立,避免干扰。并发事务的独立性多个用户同时转账,结果不会相互影响。
持久性一旦事务提交,结果将永久保存。数据的可靠性

转账成功后,账户余额的改变不会因系统崩溃而丢失

Java 事务(@Transactional)?

@Transactional就是事务的具体应用,在代码中打上该标签,表示开启一个事务,若事务中有异常(RuntimeException)发生,则回滚所有对数据库的操作

 数据库中的的事务和java中事务的区别?

  • 相似之处:核心目的都是为了确保数据的一致性和完整性。隔离级别的概念在Java中的与数据库都是一样的;
  • 不同之处:事务的范围是以方法为单位,而数据库中是以一个或多个数据库操作,可能会有多表;数据库中没有指定传播行为,可通过代码的编写来决定的传播行为;而java中就有具体的传播行为可以指定。

必须明确一点:抛出的异常被打上@Transactional的方法接收到才会进行回滚操作。

以下是@Transactional的具体使用方法:

@Transactional(rollbackFor = Exception.class) 
//若这个异常抛出,取消该删除操作
public void delete(Integer id) throws Exception {
     
            //根据部门id删除部门信息
      
            //模拟:异常
            if(true){
                throw new Exception("出现异常了~~~");
            }
            
            //删除部门下的所有员工信息
   
 }

rollBackFor属性

rollBackFor指定事务回滚的异常条件,比如指定IOException时,事务报该IOException异常时才回滚;

@Transactional不同的含义

  方法
  - 当前方法交给spring进行事务管理
  类
  - 当前类中所有的方法都交由spring进行事务管理
  接口
  - 接口下所有的实现类当中所有的方法都交给spring 进行事务管理

一个问题

A、B方法都用@Transactional标签表示开启一个事务,A方法调用B时,是开启两个事务还是共用一个事务呢?

默认下传播行为是REQUIRED
A、B方法共用一个事务。若A调用B,B中抛出异常,则A与B的所有数据库操作全部回滚。

示例:


    @Transactional
    public void outerMethod()throws {
        //增删改查

        innerMethod();

        //增删改查
    
}
    @Transactional
    public void innerMethod() {
        //插插×
        

        //ssss

        //删删删

        //抛异常
        int a=1/0;
    }

可以通过指定事务的传播行为的,决定开启一个事务还是开启两个事务,或者说直接挂起事务

事务的传播行为定义了A方法在执行时如何与B方法的事务进行交互。

简单来说,它决定了一个方法在调用另一个方法时,该如何处理事务。

propagation属性

以下是@Transactional的属性propagation的属性值,指定属性值可以确定具体的传播行为:

属性值含义
REQUIRED

A、B都打上注解@TransA、B方法共用一个事务。
【默认值】需要事务;

REQUIRES_NEW

A、B都打上注解@TransA、B方法各开一个事务。

需要新事务;

SUPPORTS支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED不支持事务,在无事务状态下运行,如果当前存在已有事务,则 挂起 当前事务
下面有具体代码解释;
MANDATORY必须有事务,否则抛异常
NEVER必须没事务,否则抛异常

传播行为NOT_SUPPORTED

若要简单的理解NOT_SUPPORTED,即在方法中指定该行为后,方法为无事务状态,该方法中的数据库操作永远不会回滚(写sql写错了还是会回滚的,数据库自带的回滚)

但既然是为无事务状态,为什么不直接把@Transational直接给删掉呢?

提示:回滚的前提一定是开启事务的方法捕获到了异常;

例子如下:

 若在innerMethod中不加@Transational注解:

/**
     *innerMethod中的异常只要抛出并被outerMethod接收到,那么innerMethod中的数据库操作就会回滚
     *就假如在innerMethod中直接使用try..catch..捕获异常,结束掉异常处理的话,那么innerMethod中的
     *数据库操作就不会回滚了;
     */
    @Transactional
    public void outerMethod()throws Exception {
        //删除  

        innerMethod();

    
        //插入
    }
 
 
    public void innerMethod() throws Exception{
        //插插×
        
        throws new Exception();

        //ssss

        //删删删
    } 

未捕获的异常如果 innerMethod 中抛出的异常没有被捕获,并且被 outerMethod 接收到(即 outerMethod 调用 innerMethod),那么 innerMethod 中的数据库操作会回滚。这是因为 Spring 的事务管理器会检测到未处理的异常,并决定回滚事务。

捕获异常如果在 innerMethod 中使用 try..catch 捕获了异常,并在 catch 块中处理了这个异常(例如,记录日志或打印消息),那么这个异常不会传播到 outerMethod。因此,Spring 将不会回滚 innerMethod 中的数据库操作,因为它认为该方法已成功完成(尽管在逻辑上可能并不是这样)。

innerMethod中加入事务注解并指定传播行为属性值为NOT_SUPPORTED;

    @Transactional
    public void outerMethod() throws Exception {
        //删除  

        innerMethod();

        //在这抛异常了;outerMethod中数据库操作回滚,innerMethod中的数据库操作不受影响。

        //插入
    }
 
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void innerMethod() throws Exception{
        //插插×

     
        //又抛异常了;outerMethod事务挂起状态,innerMethod在无事务状态下执行,上面的“插插×”                    
        //操作不会回滚;
        throw new Exception();
      

        //ssss
        //删删删
    }

事务的挂起

 代码中提到了挂起,什么叫挂起?

挂起指的是将当前的状态保存下来,然后将其从执行上下文中移除。在事务管理中,这意味着将当前事务的所有操作和状态记录下来,但不再对其进行处理。

用人话打个比方就是说:你看电影的时候按下了暂停键。此时暂停看电影这一行为的操作就叫挂起;奖励完自己后,再按下播放键,继续看电影。

outerMethod在调用 innerMethod 时,由于innerMethod 指定了传播行为NOT_SUPPORTED,事务被挂起,相当于给事务按下了暂停键,但事务中还记录着outerMethod对数据库的操作,暂时保存。这意味着在 outerMethod 中的所有数据库操作不会被提交,直到事务恢复(按下播放键),即innerMethod 在无事务的状态下运行完成

挂起与代码的暂停有什么不同?

暂停指的是在程序执行过程中暂时停止执行某段代码,等待某个操作完成或条件满足。暂停并不意味着状态被保存,通常是在等待某个外部事件。

调用方法(outerMethod)的执行被暂停,直到被调用的方法执行(innerMethod)完毕并返回。(堆栈中的调用方法入栈,结束方法出栈)

public void outerMethod() {
    //A代码行
    //B代码行
    
    //在调用这个方法时,整个代码像是被暂停了一样,只有执行完innerMethod中的所有代码才会继续运行;
    innerMethod(); 
    
    //C代码行
}


public void innerMethod() {
    
    //A代码行
    //B代码行
    //C代码行

}

其实也能用一句话讲明白:

事务的挂起是对事务的暂停,就好像事务不存在了一样而代码的暂停是指在程序执行过程中临时中断执行,等待某个条件满足或事件发生,之后继续执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值