最近在开发过程当中遇到了一个问题,当在Controller中调用Service中A()方法,A方法内部又调用Service中B()方法,因为A方法中只有查询操做因此没有加事务控制,B方法中含有屡次修改操做因此增长了@Transactional注解,结果在A方法调用完B方法后,程序报错了,可是B方法中修改操做的数据居然成功了,我擦~什么鬼,因而开启了探索Spring事务之路,直接上示例。java
示例1:A方法无事务,B方法加事务spring
@RestController public class Controller{ @Autowired private StudentcardService studentcardService; @RequestMapping(value = "/test/{id}}", method = RequestMethod.GET) public Response queryStudentCard(@PathVariable("id") String id) { studentcardService.updateA(id); } }
@Service public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override public void updateA(String id) { //先去调用内部方法B this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override @Transactional public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } }
访问后执行结果以下:app
然而事务并无起做用~接着进行测试ide
示例2:将A方法加事务,B方法不加事务测试
@Service public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } }
访问后执行结果以下:this
事务起做用了,都没有修改为功!接下来咱们来个加强版,加上try后看下会有怎样的效果spa
示例3:A方法加事务,B方法没有事务,可是在A调用B方法时用try进行包裹,B方法中有错误.net
@Service public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B try { this.updateB(id); }catch (Exception e){} StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } }
访问后执行结果以下:线程
因为报错被try包起来了,因此数据都插入了!那若是将报错信息放到执行完方法B后呢,会怎样呢?3d
示例4:A方法加事务,B方法没有事务,可是在A调用B方法时用try进行包裹,A方法中有错误
@Service public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B try { this.updateB(id); }catch (Exception e){} StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); } }
访问后执行结果以下:
哇塞,数据都没有插入呢!这是由于在事务提交前报错了,事务所有rollback了,下面言归正传,示例1为什么不能成功呢?因而查询各类资料终于找到了原因,并对示例1进行改造
示例5:A方法无事务,B方法加事务
@Service @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override public void updateA(String id) { //先去调用内部方法B StudentcardService studentcardService = (StudentcardService) AopContext.currentProxy(); studentcardService.updateB(id); //this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override @Transactional public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } }
访问后执行结果以下:
哈哈,数据没有进行修改,事务起做用了!
下面说下具体对缘由:
示例1 事务没有起做用,是因为Spring事务本质是基于AOP代理来实现的,当Controller调用Service的方法A是基于proxy的,因此会切入,可是方法A在调用方法B时,属于类内部调用,即便方法B上加上了@Transactional注解,但没有Spring代理了,因此不受事务控制,天然事务不会生效。
示例2 事务能够起做用是因为事务的传播行为致使的,默认事务的传播行为为:PROPAGATION_REQUIRED 。方法A标注了注解@Transactional ,执行的时候传播给方法B,由于方法A开启了事务,线程内的connection的属性autoCommit=false,而且执行到方法B时,事务传播依然是生效的,获得的仍是方法A的connection,autoCommit仍是为false,因此事务生效;反之,若是方法A没有注解@Transactional 时,是不受事务管理的,autoCommit=true,那么传播给方法B的也为true,执行完自动提交,即便B标注了@Transactional 事务也是不起做用的。
示例5 事务又能够起做用的,是因为咱们在方法A调用方法B时,先获取到了Service的当前代理,而后用当前代理去调用方法B,因此事务固然会生效了~
顺便补充下事务的传播行为,事务的传播行为是为了解决业务层方法之间相互调用,产生的事务应该如何进行传递的问题。spring有以下7种传播行为:
一、PROPAGATION_REQUIRED:支持当前事务,若是当前不存在事务则新建一个。
二、PROPAGATION_SUPPORTS:支持当前事务,若是不存在,就不使用事务。
三、PROPAGATION_MANDATORY:支持当前事务,若是不存在,则抛出异常。
四、PROPAGATION_REQUIRES_NEW:若是当前有事务存在,挂起当前事务,建立一个新的事务。
五、PROPAGATION_NOT_SUPPORTED:以非事务方式运行,若是当前有事务存在,挂起当前事务。
六、PROPAGATION_NEVER:以非事务方式运行,若是当前有事务存在,抛出异常。
七、PROPAGATION_NESTED:若是当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务能够从当前事务中单独的提交和回滚。若是当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持良莠不齐,使用时需注意。