【无标题】

一.Spring 编程式事务(了解)

Spring ⼿动操作事务和 MySQL 操作事务类似, 有 3 个重要操作步骤:
  • 开启事务(获取事务)
  • 提交事务
  • 回滚事务
SpringBoot 内置了两个对象:
1. DataSourceTransactionManager 事务管理器. ⽤来获取事务(开启事务), 提交或回滚事务
2. TransactionDefinition是Spring框架中用于定义事务属性的核心接口。它允许开发者定制事务的各种属性,如隔离级别、传播行为、超时时间以及是否只读,从而实现对数据库操作的精确控制和优化。
我们还是根据代码的实现来学习:
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
    // JDBC 事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    // 定义事务属性
    @Autowired
    private TransactionDefinition transactionDefinition;
    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    public String registry(String name,String password){
        // 开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager
                .getTransaction(transactionDefinition);
        //⽤⼾注册
        userService.registryUser(name,password);
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
        //回滚事务
        //dataSourceTransactionManager.rollback(transactionStatus);
        return "注册成功";
    }
}
代码解读:
这段代码定义了一个Spring Boot控制器 UserController,它包含了一个用于用户注册的方法 registry。在 registry方法中,通过编程式事务管理,手动开启了数据库事务,调用了 UserServiceregistryUser方法进行用户注册,如果注册成功则提交事务,如果在注册过程中遇到异常则回滚事务。
观察事务提交
//提交事务
dataSourceTransactionManager.commit(transactionStatus);
运⾏程序:
观察数据库的结果, 数据插⼊成功.
观察事务回滚
//回滚事务
dataSourceTransactionManager.rollback(transactionStatus);

运⾏程序:

 观察数据库, 虽然程序返回"注册成功", 但数据库并没有新增数据.

二.Spring 声明式事务 @Transactional

声明式事务的实现很简单, 只需要在需要事务的⽅法上添加 @Transactional 注解就可以实现了.
⽆需⼿动开启事务和提交事务, 进⼊⽅法时⾃动开启事务, ⽅法执⾏完会⾃动提交事务, 如果中途发⽣了没有处理的异常会⾃动回滚事务.
@Transactional注解是 Spring 框架提供的一个核心注解,用于声明一个方法或类需要事务支持。当 Spring 容器扫描到带有  @Transactional注解的类或方法时,Spring 会为这个类或方法创建一个代理对象,并在代理对象中嵌入事务管理的逻辑。

以下是@Transactional注解的一些关键特性和用法:

  1. 方法级别的事务管理@Transactional可以注解在方法上,表示该方法将运行在事务的上下文中。如果方法执行成功,则自动提交事务;如果方法抛出运行时异常(RuntimeException 或其子类),则自动回滚事务。

  2. 类级别的事务管理@Transactional 也可以注解在类上,表示该类中的所有公共方法都将启用事务管理。但是,如果类中的某个方法不希望使用事务管理,可以在该方法上使用 @Transactional(propagation = Propagation.NOT_SUPPORTED) 来声明。

  3. 事务属性@Transactional,注解提供了多个属性来定义事务的行为,包括传播行为(propagation)、隔离级别(isolation)、超时时间(timeout)、只读标志(readOnly)以及回滚规则(rollbackFornoRollbackFor)等。

  4. 代理机制:Spring 通过代理机制来实现声明式事务管理。对于基于接口的代理(JDK 动态代理),Spring 会为目标接口创建一个代理接口实现;对于类代理(CGLIB 代理),Spring 会为目标类创建一个子类。事务管理的逻辑被嵌入到这些代理对象中。

  5. 异常回滚:默认情况下,@Transactional 注解的事务会在遇到运行时异常时回滚。但是,开发者可以通过 rollbackFor 和 noRollbackFor 属性来自定义回滚规则。

  6. 事务的传播行为@Transactional的 propagation 属性定义了事务的传播行为,即当前事务方法应该如何与存在的事务进行交互。例如,Propagation.REQUIRED 表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

使用 @Transactional注解可以极大地简化事务管理的代码,使开发者能够更专注于业务逻辑的实现,而不是事务管理的细节。然而,也需要注意,由于事务管理是通过代理实现的,因此有些情况下(如类内部的方法调用)可能无法触发事务管理逻辑。此外,还需要合理配置 Spring 的事务管理器(如 DataSourceTransactionManager)和数据源,以确保事务能够正常工作。

我们来看代码实现:
@RequestMapping("/trans")
@RestController
public class TransactionalController {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //⽤⼾注册
        userService.registryUser(name,password);
        return "注册成功";
    }
}
运⾏程序, 发现数据插⼊成功.
修改程序, 使之出现异常
@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");
        //强制程序抛出异常
        int a = 10/0;
        return "注册成功";
    }
}
运⾏程序:
发现虽然⽇志显⽰数据插⼊成功, 但数据库却没有新增数据, 事务进⾏了回滚.

@Transactional 作⽤

@Transactional 可以⽤来修饰⽅法或类:
修饰⽅法时: 只有修饰public ⽅法时才⽣效(修饰其他⽅法时不会报错, 也不⽣效)
修饰类时: 对 @Transactional 修饰的类中所有的 public ⽅法都⽣效.
⽅法/类被 @Transactional 注解修饰时, 在⽬标⽅法执⾏开始之前, 会⾃动开启事务, ⽅法执⾏结束
之后, ⾃动提交事务.
如果在⽅法执⾏过程中, 出现异常, 且异常未被捕获, 就进⾏事务回滚操作.
如果异常被程序捕获, ⽅法就被认为是成功执⾏, 依然会提交事务.
修改上述代码, 对异常进⾏捕获
    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return "注册成功";
    }
运⾏程序, 发现虽然程序出错了, 但是由于异常被捕获了, 所以事务依然得到了提交.
如果需要事务进⾏回滚, 有以下两种⽅式:
1. 重新抛出异常
    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            //将异常重新抛出去
            throw e;
        }
        return "注册成功";
    }
2. ⼿动回滚事务
使⽤ TransactionAspectSupport.currentTransactionStatus() 得到当前的事务, 并
使⽤ setRollbackOnly 设置 setRollbackOnly
    @Transactional
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            // ⼿动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return "注册成功";
    }

2.1@Transactional 详解

通过上⾯的代码, 我们学习了 @Transactional 的基本使⽤. 接下来我们学习 @Transactional
注解的使⽤细节.
我们主要学习 @Transactional 注解当中的三个常⻅属性:
1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型
2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT
3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

2.1.1 rollbackFor

@Transactional 默认只在遇到运⾏时异常和Error时才会回滚, ⾮运⾏时异常不回滚. 即
Exception的⼦类中, 除了RuntimeException及其⼦类.

 我们上⾯为了演⽰事务回滚, ⼿动设置了程序异常

int a = 10/0;
接下来我们把异常改为如下代码
   @Transactional
    @RequestMapping("/r2")
    public String r2(String name,String password) throws IOException {
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");
        if (true){throw new IOException();
        }
        return "r2";
    }
运⾏程序:

发现虽然程序抛出了异常, 但是事务依然进⾏了提交

 

如果我们需要所有异常都回滚, 需要来配置 @Transactional 注解当中的 rollbackFor 属性, 通
rollbackFor 这个属性指定出现何种异常类型时事务进⾏回滚.
   @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/r2")
    public String r2(String name,String password) throws IOException {
        //⽤⼾注册
        userService.registryUser(name,password);
        log.info("⽤⼾数据插⼊成功");if (true){
             throw new IOException();
             }
         return "r2";
         }
运⾏程序:

发现虽然程序抛出了异常, 但是事务依然进⾏了提交

结论:
在Spring的事务管理中,默认只在遇到运⾏时异常RuntimeException和Error时才会回滚.
如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值