概念
在MySQL中介绍过,当同一时间出现一起读写数据的情况,可能会导致最终的结果出错,因此可以使用事务来提高隔离级别
 而Spring中也可以实现事务
手动添加事务
使用SpringBoot中的DataSourceTransactionManager对象可以获取事务,提交事务,回滚事务
TransactionDefinition是事务的属性,在获取事务时需要传入这个参数
而TransactionStatus则是在获取事务时获取的对象,最终回滚时使用
@RestController
@RequestMapping("url")
public class 实现类 {
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @RequestMapping("url")
    public 相关方法() {
        //开启事务
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
        //处理事务
        //回滚事务
        transactionManager.rollback(transactionStatus);
    }
 
例如:
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @RequestMapping("/add")
    public int add(UserInfo userInfo) {
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        //开启事务
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        //回滚事务
        transactionManager.rollback(transactionStatus);
        return result;
    }
}
 
在页面中,传输了参数username = 1, password = 1
 但是在数据库中,并没有出现这行数据
 
自动添加事务
使用@Transactional注解可以快速添加事务
@Transactional
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
            !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
    }
    return result;
}
 
如果没有异常,那么会自动提交事务,如果有异常,事务会进行回滚
例如:在类中添加空指针异常
 如果没有添加@Transactional那么在异常出现前数据已经上传到数据库
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
            !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
	int num = 1 / 0;
    return result;
}
 

 
 而加了@Transactional注解,就可以自动回滚事务,数据库中就没有这行数据了
 
 
 而如果我们添加了try,catch语句,spring就不会感知到异常的存在了,也就不会进行事务的回滚了
@Transactional //声明式事务(没有错误自动提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
            !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
    try{
        int num = 1 / 0;
    } catch (Exception e){
        e.printStackTrace()
    }
    return result;
}
 

 
 如果我们还想要事务进行自动回滚,那么可以使用throw将异常抛出,这样spring就能再次感知到异常的存在了
@Transactional //声明式事务(没有错误自动提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
            !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
    try{
        int num = 10 / 0;
    } catch (Exception e){
        e.printStackTrace();
            throw e;
    }
    return result;
}
 

 
如果不想要页面出现报错信息,那么可以不添加throw语句,而是手动回滚事务
@Transactional //声明式事务(没有错误自动提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
            !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
    try{
        int num = 10 / 0;
    } catch (Exception e){
        e.printStackTrace();
      	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
    }
    return result;
}
 

 
事务隔离级别设置
当不同事务同时对数据库进行数据操作,就会出现问题,因此可以针对不同的情景设置不同的隔离级别
 
在@Transactional注解中可以设置事务的隔离级别
 
| 代码 | 含义 | 问题 | 
|---|---|---|
| Isolation.DEFAULT | 默认级别 以数据库隔离级别执行 | |
| Isolation.READ_UNCOMMITTED | 读未提交 可以读未提交数据 | 存在脏读问题 | 
| Isolation.READ_COMMITED | 读已提交 只能读到已经提交事务 | 存在不可重复读问题 | 
| Isolation.REPEATABLE_READ | 可重复读 | 存在幻读问题 | 
| Isolation.SERIALIZABLE | 串行化 | 性能低 | 
事务传播机制
在项目中会存在多个方法链式调用,而每一个方法都有可能存在事务,因此存在不同的事物传播机制
 
 分别有如下几种事物传播级别:
| 代码 | 说明 | 
|---|---|
| Propagation.REQUIRED | (默认)如果当前存在事务则加入事务,都则创建一个事务 | 
| Propagation.SUPPORTS | 如果当前存在事务就加入事务,否则就以非事务方式运行 | 
| Propagation.MANDATORY | 如果当前存在事务就加入事务,否则抛出异常 | 
| Propagation.REQUIRES_NEW | 不管当前有没有事务,都创建并使用新事务 | 
| Propagation.NOT_SUPPORTED | 不管当前有没有事务,都以非事务方式运行 | 
| Propagation.NEVER | 以非事务形式运行,如果当前存在事务就抛出异常 | 
| Propagation.NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务运行,当作REQUIRED | 














![Mysql问题:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause](https://img-blog.csdnimg.cn/1066a58e1b5d4b119df843e604651406.png)




