Spring事务

2023-12-14 00:27:34

为什么需要事务

什么是事务

将一组操作封装成一个执行单元,要么全部执行,要么全部不执行

为什么需要事务

例如转账场景,张三有1000块,给李四转500,张三账户-500;李四账户+500.如果没有事务,可能张三扣款执行了,李四加钱操作没执行。而使用事务可以保证转账操作要么一起执行,要么不执行

Spring中事务的实现

1.通过代码方式手动实现事务

@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);
        //手动设置时间
        userinfo.setCreatetime(LocalDateTime.now().toString());
        userinfo.setUpdatetime(LocalDateTime.now().toString());
        int result = userService.add(userinfo);

        System.out.println("添加:" + result);

        transactionManager.rollback(transactionStatus);//传要回滚哪个事务
        //提交事务
        //transactionManager.commit(transactionStatus);
        return result;
    }
}

2.通过注解方式实现声明事务、

只需要在方法上或类上添加@Transacational注解即可(修饰方法时只能作用于public方法,修饰类时,类里面所有publice方法都生效),无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完成后自动提交事务,如果中途有被JVM捕捉到异常会自动回滚事务

//使用@Transactional声明事务
    @RequestMapping("/insert")
    @Transactional
    public int insert(Userinfo userinfo) {
        if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername())
            || !StringUtils.hasLength(userinfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userinfo);
        //int num = 10 / 0; 发生算数异常,会自动回滚
        return result;
    }

注意:当程序有try-catch之后,即使程序发送异常,事务也不会自动回滚

解决方案有两种

  1. 将异常抛出
//使用@Transactional声明事务
    @RequestMapping("/insert")
    @Transactional
    public int 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) {
            throw e;//把异常抛给Jvm让它来处理
        }
        return result;
    }
  1. 使用代码手段回滚事务
@RequestMapping("/insert")
    @Transactional
    public int 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) {
            //使用代码手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

@Transactional工作原理

Transactional是基于AOP思想实现的,AOP又是基于动态代理实现的。如果目标对象实现了接口,默认情况下采用JDK的动态代理;如果目标对象没实现接口,会使用CGLIB动态代理。
image.png

Spring事务的隔离级别

  1. @Transactional(isolation=Isolation.DEFAULT) :以连接的数据库的事务隔离级别为主
  2. @Transactional(isolation=Isolation.READ_UNCOMMITTED):读未提交,可以读取未提交的事务,存在脏读问题。
  3. @Transactional(isolation=Isolation.READ_COMMITTED):读以提交,只能读已提交的事务,解决了脏读,但存在不可重复读问题(两次读取到的数据可能不同)
  4. @Transactional(isolation=Isolation.REPEATABLE_READ):可重复读,解决了不可重复读问题,但存在幻读(一次事务两次查询得到的结果集不同,因为在两次查询中另一个事务新增了一部分数据)
  5. @Transactional(isolation=Isolation.SERIALIZABLE):解决所有并发问题,但是性能过低。

事务的传播机制

事务的隔离级别解决的是多个事务同时调用数据库的问题,而事务的传播机制解决的是一个事务在多个节点(方法)中传递的问题。

Spring事务的7种传播机制

image.png
以默认的传播机制举例:

@RequestMapping("/insert")
    @Transactional(propagation = Propagation.REQUIRED)//设置事务传播机制
    public int insert(Userinfo userinfo) {
        if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername())
            || !StringUtils.hasLength(userinfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userinfo);//插入一条记录
        //说明数据已经插入成功
        if(result > 0) {
            logService.add();
        }
        return result;
    }
@Service
public class LogService {
    //如果有事务就加入事务,不存在就创建事务
    @Transactional(propagation = Propagation.REQUIRED)
    public int add() {
        try {
            int num = 10 / 0;
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return 1;
    }
}

Controller层和Service层都设置了事务的传播机制为:如果有事务就加入事务,没有事务就创建事务。Controller层调用Service层,Controlller层有事务,所以Service层就加入它,变成一个整体,LogService抛出算术异常,整个事务都要回滚,导致原本已经插入成功的记录也要回滚。

文章来源:https://blog.csdn.net/weixin_61427900/article/details/134909167
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。