0
点赞
收藏
分享

微信扫一扫

javaEE-02-servlet

目录

🚩事务

🎈为什么需要事务?

🎈事务的操作

🚩Spring 中事务的实现

🎈数据准备

🎈Spring 编程式事务(了解)

🎈Spring 声明式事务 @Transactional

🍭@Transactional 详解

📝rollbackFor

❗结论(异常捕获问题)

📝spring事务隔离级别

📝Spring 事务传播机制

✅Propagation.REQUIRED(事务之间互相影响)

✅Propagation.REQUIRED_NEW(新建事务,互不影响)

Propagation.NEVER (不⽀持当前事务, 抛异常)

Propagation.NESTED(嵌套事务)

👩🏻‍💻NESTED和REQUIRED有什么区别?

🚩总结


🚩事务

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。


🎈为什么需要事务?

我们在进行程序的开发的时候,也会有事务的需求。


🎈事务的操作


🚩Spring 中事务的实现

前⾯课程我们讲了MySQL的事务操作, Spring对事务也进⾏了实现

在学习事务之前,我们先准备数据和数据的访问代码。


🎈数据准备


🎈Spring 编程式事务(了解)

@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器
    @Autowired
    private TransactionDefinition transactionDefinition;//定义事务属性
    @Autowired
    private UserService userService;
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        //开启事务
        TransactionStatus transaction=dataSourceTransactionManager.getTransaction(transactionDefinition);
        //用户注册
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //提交事务
        dataSourceTransactionManager.commit(transaction);
        return true;
    }
}

提交事务:

事务回滚:数据库信息未更新

@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器
    @Autowired
    private TransactionDefinition transactionDefinition;//定义事务属性
    @Autowired
    private UserService userService;
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        //开启事务
        TransactionStatus transaction=dataSourceTransactionManager.getTransaction(transactionDefinition);
        //用户注册
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //回滚事务
        dataSourceTransactionManager.rollback(transaction);
        //提交事务
        dataSourceTransactionManager.commit(transaction);
        return true;
    }
}


🎈Spring 声明式事务 @Transactional

声明式事务的实现很简单, 只需要在需要事务的⽅法上 添加 @Transactional 注解 就可以实现了.

⽆需⼿动开启事务和提交事务, 进⼊⽅法时⾃动开启事务, ⽅法执⾏完成会⾃动提交事务, 如果中途发⽣了没有处理的异常会⾃动回滚事务.



如果程序中,出现错误,我们依旧需要事务进行回滚,不提交事务,该如何处理呢?

如果需要事务进⾏回滚, 有以下两种⽅式:


🍭@Transactional 详解


📝rollbackFor

@Transactional 默认只在遇到运⾏时异常和Error时才会回滚, ⾮运⾏时异常不回滚. 即

Exception的⼦类中, 除了RuntimeException及其⼦类.

🔴我们上面说,如果程序有异常,我们需要给回滚,那么就有两种方式进行回滚,第一就是手动回滚,第二就是重新抛出异常。

🔴但是再第二种方法中, @Transactional默认只在遇到运行时异常和Error时才会回滚,上面我们举例的是int a=1/0这是算术异常属于运行时异常中的一类,但是如果我们 抛出IOException异常也就是非运行时异常时,事务就直接提交,不回滚。接下来我们由代码显示。

@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password) throws IOException {
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            throw new IOException();
        }
        return true;
    }
}

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


解决: 如果我们需要所有异常都回滚, 需要来配置 @Transactional 注解当中的 rollbackFor 属性, 通 过 rollbackFor 这个属性指定出现何种异常类型时事务进⾏回滚.

@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/register")
    public Boolean register(String userName, String password) throws IOException {
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            throw new IOException();
        }
        return true;
    }
}


❗结论(异常捕获问题)


📝spring事务隔离级别


📝Spring 事务传播机制

事务传播机制就是: 多个事务⽅法存在调⽤关系时, 事务是如何在这些⽅法间进⾏传播的

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题

⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题


事务的传播机制分类:

@Transactional 注解⽀持事务传播机制的设置, 通过 propagation 属性来指定传播⾏为.


✅Propagation.REQUIRED(事务之间互相影响)

默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则创建⼀个新的事务.

对应的UserService和LogService都添加上

@Transactional(propagation = Propagation.REQUIRED )

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
   @Transactional(propagation = Propagation.REQUIRED)
    public Integer insert(String userName, String password) {
        return userInfoMapper.insert(userName,password);
    }
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insert(String userName, String op) {
        Integer result= logInfoMapper.insertLog(userName,op);
        return result;
    }
}
@RequestMapping("/user2")
@RestController
@Slf4j
public class UserController2 {
    @Autowired
    private UserService userService;
    @Autowired
    private LogService logService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        /**
         * 用户表和日志表的插入,理应在service中完成
         */
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //插入日志表
        Integer logResult=logService.insert(userName,"用户注册");
        System.out.println("插入日志表,result:"+logResult);
        return true;
    }
}


✅Propagation.REQUIRED_NEW(新建事务,互不影响)

创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的 内部⽅法都会新开启⾃⼰的事务 , 且开启的事务 相互独⽴, 互不⼲扰..

所以,userservice中的方法就会创建一个事务,logservice中的方法也会创建一个事务,那么就相当于有三个事务。三个事务相互独立,互不干扰。

UserService方法中的事务程序执行正常

LogService方法中的事务程序执行异常

我们看看是否是相互独立提交和回滚的。

如果LogSerice失败,UserService成功,两个方法是独立的,互不影响。用户表更新,日志表更新失败。


✅NEVER (不⽀持当前事务, 抛异常)

修改UserService 中对应⽅法的事务传播机制为 Propagation. NEVER

程序执⾏报错, 没有数据插⼊。在以后应用中,如果不希望该事务加入,那么就用never。


✅NESTED(嵌套事务)

将上述UserService 和LogService 中相关⽅法事务传播机制改为 Propagation. NESTED

1.三个事务全部成功

三个事务全部成功,那么结果就和Required一样,用户表和日志表数据库都更新成功。


2.三个事务中部分失败

LogService中事务程序会出现异常,UserService事务程序没有异常。

三个事务都回滚,结果也是和Required一样,用户表和日志表更新失败。

register事务可以认为是⽗事务, 嵌套事务是⼦事务. ⽗事务出现异常, ⼦事务也会回滚, ⼦事务出现异常, 如果不进⾏处理, 也会导致⽗事务回滚。


👩🏻‍💻NESTED和REQUIRED有什么区别?

和REQUIRED区别就是,NESTED可以实现部分回滚。我们在上面LogService方法出现错误,如果没有进行捕获事务会和REQUIRED一样进行回滚,我们上面也说明了异常捕获问题,如果有异常捕获了那么就正常提交,但是我们希望异常捕获后回滚,那么就需要我们手动回滚异常或者抛出异常。这种做法会和REQUIRED一样嘛?

首先看 NESTED事务传播机制

用户表更新,日志表未更新。实现了部分回滚。


对比REQUIRED传播事务机制

重新运⾏程序, 发现⽤⼾表和⽇志表的数据添加都失败了.

REQUIRED 如果回滚就是回滚所有事务, 不能实现部分事务的回滚. (因为属于同⼀个事务)



🚩总结


举报

相关推荐

0 条评论