Spring事务

为什么需要事务

什么是事务

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

为什么需要事务

例如转账场景,张三有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抛出算术异常,整个事务都要回滚,导致原本已经插入成功的记录也要回滚。

相关推荐

  1. Spring事务

    2023-12-10 17:46:04       16 阅读
  2. Spring 事务管理

    2023-12-10 17:46:04       44 阅读
  3. Spring 声明式事务

    2023-12-10 17:46:04       40 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-10 17:46:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-10 17:46:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-10 17:46:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-10 17:46:04       20 阅读

热门阅读

  1. Linux 基本了解

    2023-12-10 17:46:04       36 阅读
  2. NVMe over Fabrics with SPDK with iRDMA总结 - 2

    2023-12-10 17:46:04       27 阅读
  3. 说说设计体系、风格指南和模式库

    2023-12-10 17:46:04       30 阅读
  4. springboot——helloworld入门

    2023-12-10 17:46:04       27 阅读
  5. Python3 基本数据类型 ----20231209

    2023-12-10 17:46:04       31 阅读
  6. mysql中information_schema.tables字段说明

    2023-12-10 17:46:04       41 阅读
  7. GO设计模式——1、简单工厂模式(创建型)

    2023-12-10 17:46:04       39 阅读
  8. 开源软件:JumpServer、DataEase、MeterSphere

    2023-12-10 17:46:04       44 阅读