Spring中如何使用事务?

前置知识

什么是事务?

它指的是一组操作,这些操作要么全部成功,要么全部失败。

值得注意的是:事务能否生效取决于数据库引擎是否支持事务。比如我们 MySQL 中默认使用的是 innodb,但是,如果将存储引擎改成 myisam,那么程序就不支持事务了。

事务的四大特性(ACID)

  • 原子性:事务执行的过程中,要么全部完成,要么全部不完成,不会在中间的某一个环节结束。事务的原子性是有 undo log 日志保证的。
  • 一致性:事务执行前后,数据库的状态必须保持一致,事务的一致是由 原子性,隔离性,一致性共同保证的
  • 隔离性:允许多个事务并发并发的读写数据库,防止多个事务并发的读写同一份数据的时候,出现数据不一致的情况,事务的隔离性是由 MVCC 和 锁 保证的
  • 持久性:事务完成后,对数据的修改就是永久的,不会因为机器的故障而丢失,持久性是由 redo log 日志保证的。

Spring 事务实现的机制

Spring 事务管理是通过 AOP 来实现的,AOP 又是使用动态代理实现的。Spring 在运行时为目标对象创建一个代理对象,代理对象负责拦截对目标对象方法的调用。在方法调用之前和之后,代理对象可以执行额外的逻辑,例如开始和提交或回滚事务。

细说如何Spring中如何使用事务?

在 Spring 中,使用事务的方式常用的有以下两种:

1. 声明式事务

声明式事务是指通过配置或注解的方式来管理事务,比如加上 @Transactional,框架就会自动的帮助我们管理事务

@Transactional 注解 使用的最多,因为使用起来非常的方便,对代码的入侵性比较小,更易于拓展和维护。

示例:

@Service
public class MyService {

    @Transactional
    public void myTransactionalMethod() {
        // 业务逻辑
    }
}

2. 编程式事务

编程式事务是指开发者在代码中需要手动的开启,提交,回滚事务。

Spring 框架中,事务管理相关最重要的 3 个接口:

PlatformTransactionManager:负责处理事务的生命周期,提供事务的创建、提交和回滚功能。

主要方法包括:

public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

DefaultTransactionDefinition:用于定义事务的属性,比如传播行为、隔离级别等。

示例:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 设置传播行为
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 设置隔离级别
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

TransactionStatus:表示事务的状态,可以用于提交或回滚事务。

如何在代码中使用:

@Service
public class MyService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void myTransactionalMethod() {
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置事务传播行为
        def.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); 
        
        // 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 业务逻辑
            // .....
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 出现异常,回滚事务
            transactionManager.rollback(status);
            // 重新抛出异常
            throw e; 
        }
    }
}

注意:编程式事务的代码并不是唯一的,还有其他的方式,这里仅提供一个方法作为参考。

编程式事务和声明式事务的应用场景?

编程式事务的应用场景:

  1. 动态控制事务边界:编程式事务可以根据具体业务逻辑和条件来灵活地决定事务的边界。
  2. 控制事务粒度,避免数据库事务长时间执行,也就是大家常说的避免大事务或长事务。

那问题来了,怎么理解控制事务的边界?控制事务粒度?

举个例子:我在 method 使用了 @Transactional(声明式事务)

@Service
public class MyService {

@Transactional
 public void method() {
     // 业务逻辑 ......
     
  }
}

我们知道@Transactional 注解,是使用 AOP 实现的,本质就是在目标方法的前和后拦截,也就是生成代理类对象,在调用目标方法前,开启事务,方法结束后提交事务,出现异常回滚事务。

我来写一个伪代码帮助你理解。

public class proxyService {

public void proxyMethod() {
     // 目标方法前,开启事务
    begin();
    try {
        // 调用目标方法,也就是上面例子中的 method()
        method();
        // 方法结束后提交事务
        commit();
    } catch (Exception e) {
        // 出现异常,回滚事务
        rollback();
    }
  }
}

好了这样子有什么问题呢?

@Transactional注解包裹在的整个方法,假设极端情况下,我这个方法执行很长时间,是不是就卡在那里了,也就是一个长事务。

怎么解决??

我只对需要事务的那一部分代码,进行事务管理不就好了,其他的业务代码,我不管。

伪代码示例:

public void demo() {
     // 前面一堆业务逻辑 ......

    begin();
    try {
    // .....数据库操作
        
    // 提交事务
    commit();
    } catch (Exception e) {
        // 出现异常,回滚事务
        rollback();
    }
     
}

所以,使用编程式事务最大的好处就是控制事务边界,精细化控制事务范围。

这时候,是不是觉得好像编程式事务更好??

不不不,你错了,其实我们平常还是注解用的多,为什么??

因为声明式事务管理更加简洁、易于维护和扩展。说白了就是想偷个懒。开个玩笑!!

其实我们使用@Transactional 注解也可控制事务边界,核心思想就是:将业务逻辑的代码和需要事务管理的代码拆分开。也就是需要事务管理的代码,单独写成一个方法 或者 放到一个类中。

伪代码如下:

@Service
public class MyService {


 public void method() {
     // 业务逻辑 ......
     save();
  }

@Transactional
public void save() {
    
}

}

好了,说了这么多,还是注解好用。

但是......

提个醒,上面还是有问题的,主要是使用@Transactional 会出现 事务失效的情况,所以说还是需要小心的。

常见@Transactional 会出现事务失效的情况可以了解一下我写的这篇文章:Spring @Transactional 你真的会用吗???-优快云博客

总结

今天了我们学习了如何在Spring中使用事务,常用的有声明式事务,也就是使用注解的方式。还有就是编程式事务,也就是写代码的形式手动的管理事务。还讲了声明式事务和编程式事务的使用场景,编程式事务,可以控制事务的边界和细度,但是对代码有入侵性,不易于维护。大多数情况下,推荐使用基于注解声明式事务管理来管理事务,因为声明式事务管理更加简洁、易于维护和扩展。如果不是特别需要,应该尽量避免使用编程式事务管理。

最后留了一个收尾的点,就是使用声明式事务的时候,会出现事务失效的情况。

好了,分享到结束了,如果觉得我写的还不错,记得给我三连哦,创作真的不容易,感谢大家的支持,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值