当前位置: 首页 > 科技观察

MySQL事务你必须知道的几个知识点!

时间:2023-03-15 09:54:23 科技观察

Transaction上一期我们讲了jpa的常用操作,查询、更新、删除等,但是如果操作数据库事务时出现异常,数据会回滚吗?让我们看一个示例UserController并添加以下代码:@GetMapping("save1")publicStringsave1(){Useruser=newUser();user.setDptId(1L);user.setName("a");user.setAge(18L);user.setEmail("a@a.com");user.setHeadImg("headImg1");this.userJpa.save(user);//模拟异常发生System.out.println(1/0);return"ok";}执行后使用postman请求localhost:8080/user/save1,可以看到java后台报错,postman前台也报错,但是数据保存了,并且新建了一个在数据中添加了record,表示即使发生了异常,数据还是会保存在数据库中,那怎么办呢?尝试向save1方法添加@Transactional注释。让我们再来一次。也报错了,但是数据库没有往里面插入新的数据。最新记录是id为7的最后一条记录,那么Transactional注解是做什么用的呢?@Transactional是声明式事务管理编程中使用的注解。该注解加在实现类或接口实现方法上,不能放在接口中。需要注意的是,这个注解只对公共方法有效。以下是此注解的属性。我们需要关注的两个属性是rollback-for和propagation。属性名称描述名称当配置文件中有多个TransactionManager时,可以使用该属性指定选择哪个事务管理器。propagation事务的传播行为,默认值为REQUIRED。isolation事务隔离,默认值为DEFAULT。timeout事务超时时间,默认值为-1。如果超过时间限制但事务还没有完成,则事务自动回滚。read-only指定事务是否为只读事务,默认值为false;为了忽略那些不需要事务的方法,比如读取数据,可以将read-only设置为true。rollback-for用于指定可以触发事务回滚的异常类型。如果需要指定多个异常类型,每个类型可以用逗号分隔。no-rollback-for抛出no-rollback-for指定的异常类型,不回滚事务。rollback-for:只回滚已执行的异常。但是我们刚才的程序没有指定异常,那么默认会回滚什么样的异常呢?稍微修改UserController中的代码,手动抛出newException("test"),然后执行postman,发现事务已经提交,没有回滚。然后我们把注解改成@Transactional(rollbackFor=Exception.class),然后执行postman,但是事务回滚了,没有提交。为什么?Spring的@Transactional注解可以很方便的启动一个事务,但是默认只有遇到运行时异常和Errors才会回滚,非运行时异常是不会回滚的,也就是说在Exception的子类中,除了RuntimeException及其子类,其他类默认不回滚。rollbackFor属性可以解决这个问题。rollbackFor=Exception.class表示Exception及其子类的异常会触发回滚,不影响Error的回滚。propagation:使用最广泛的需求是出现业务错误,但必须将日志提交到数据库。如何处理?看看下面的代码。新的日志服务类@ServicepublicclassLogService{@ResourceprivateUserJpauserJpa;@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidsaveLog(){Useruser=newUser();user.setDptId(1L);user.setName("log");user.setAge(18L));user.setEmail("log@log.com");user.setHeadImg("log");this.userJpa.save(user);System.out.println("log");}}新用户服务类:@ServicepublicclassUserService{@ResourceprivateUserJpauserJpa;@ResourceprivateLogServicelogService;@Transactional(rollbackFor=Exception.class)publicvoidsaveBiz()throwsException{System.out.println("save2");Useruser=newUser();user.setDptId(1L);用户。setName("biz");user.setAge(18L);user.setEmail("biz@biz.com");user.setHeadImg("biz");this.userJpa.save(user);//模拟保存日志this.logService.saveLog();//模拟异常thrownewException("test1");}}UserControllernewcode@GetMapping("save2")publicStringsave2()throwsException{//模拟业务运行this.userService.saveBiz();return"ok";}Postman执行,是否只插入了日志记录?该业务未插入。注意:在同一个业务类中,即使声明为Propagation.REQUIRES_NEW,也不会启动新的事务。必须调用另一个类的Propagation.REQUIRES_NEW方法。所以在例子中,UserService中使用了另一个类LogService中调用saveLog的方法。