程序中是不可能有事务的,而在Spring中,事务的实现方式有两种:程序化事务和声明式事务,而且由于程序化事务实现起来相对繁琐,声明式事务极其简单,所以在日常项目中,我们都是使用声明式事务@Transactional来实现事务。@Transactional使用起来非常简单。您只需要在类或方法中添加@Transactional关键字即可自动打开、提交或回滚事务。它的基本用法如下:@Transactional@RequestMapping("/add")publicintadd(UserInfouserInfo){intresult=userService.add(userInfo);returnresult;}@Transactional执行过程@Transactional会在方法执行前自动开启事务;方法执行成功后,会自动提交交易;如果在方法执行过程中发生异常,它会自动回滚事务。然而@Transactional,看似极其简单,却隐藏着一些“陷阱”。这些陷阱就是我们今天要聊的话题:导致@Transactional事务失败的常见场景有哪些?在开始之前,我们需要明确一个定义,什么是“失败”?本文中的“失效”指的是“失去(其)有效性”,即当@Transactional不符合我们预期的结果时,我们可以说@Transactional失效了。@Transactional失败的场景有哪些?接下来,让我们一一了解。1.非public修饰的方法当@Transactional修饰的方法为非public时,交易将无效。例如下面的代码遇到异常后不能自动回滚:@RequestMapping("/save")intsave(UserInfouserInfo){//非空校验if(userInfo==null||!StringUtils.hasLength(userInfo).getUsername())||!StringUtils.hasLength(userInfo.getPassword()))返回0;//添加操作intresult=userService.save(userInfo);System.out.println("添加受影响的行数:"+result);整数=10/0;//此处设置异常returnresult;}上面程序的运行结果如下:当程序运行异常时,我们期望事务自动回滚,即添加用户失败。但是,当我们查询数据库时,发现事务并没有执行回滚操作。数据库中的数据如下如图:2.timeout超时当在@Transactional上设置了一个很小的超时时间,如果方法本身的执行时间超过了设置的timeout超时时间,就会导致执行应该正常插入数据的方法失败了,示例代码如下:@Transactional(timeout=3)//timeoutis3s@RequestMapping("/save")intsave(UserInfouserInfo)throwsInterruptedException{//non-null验证if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())||!StringUtils.hasLength(userInfo.getPassword()))返回0;int结果=我们erService.save(userInfo);returnresult;}UserService的save方法实现如下:publicintsave(UserInfouserInfo)throwsInterruptedException{//Sleepfor5sTimeUnit.SECONDS.sleep(5);intresult=userMapper.add(userInfo);returnresult;}上面程序运行结果如下:数据库没有正确插入数据,如下图所示:3、代码中有一个try/catch。在前面@Transactional的执行过程中,我们提到过:当一个异常发生后,事务自动回滚但是,如果在程序中加入try/catch,@Transactional不会自动回滚事务。示例代码如下:@Transactional@RequestMapping("/save")publicintsave(UserInfouserInfo)throwsInterruptedException{//If(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())||!StringUtils.hasLength(userInfo.getPassword()))返回0;intresult=userService.save(userInfo);尝试{intnum=10/0;//这里设置一个异常}catch(Exceptione){}returnresult;}上面程序的运行结果如下:这时我们发现程序在查询数据库的时候并没有进行回滚操作。一条数据已经成功添加到数据库中,如下图所示:4.调用类内部的@Transactional方法。在类内部调用@Transactional修饰的方法时,事务不会生效。示例代码如下:@RequestMapping("/save")publicintsaveMapping(UserInfouserInfo){returnsave(userInfo);}@Transactionalpublicintsave(UserInfouserInfo){//非空验证if(userInfo==空||!StringUtils.hasLength(userInfo.getUsername())||!StringUtils.hasLength(userInfo.getPassword()))返回0;intresult=userService.save(userInfo);在t数=10/0;//此处设置异常returnresult;}在上面的代码中,我们在方法save中添加了@Transactional声明事务,并添加了异常代码。我们预期的结果是程序异常,事务执行自动回滚,上面程序的执行结果如下:但是,当我们查询数据库时,发现程序执行不符合我们的预期,而添加的数据不执行自动回滚,如下图所示:5.数据库不支持事务我们程序中的@Transactional只是向调用数据库发送指令:启动事务,提交事务,回滚事务,但是如果数据库本身是不支持事务的,比如MySQL中设置了MyISAM引擎,那么它本身是不支持事务的。这种情况下,即使在程序中加上了@Transactional注解,仍然不会有事务行为。会失败:非公开修改的方法;超时设置太小;在代码中使用try/catch来处理异常;在类内部调用@Transactional方法;数据库不支持事务。参考与致谢www.cnblogs.com/frankyou/p/12691463.html
