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

spring两层事务,我抛出的异常去哪了?

时间:2023-03-13 21:53:58 科技观察

系统A调用系统B进行数据同步。系统B返回一条错误消息。系统A需要回滚之前的保存,向上抛出错误信息。大致代码如下@Service("noteService")publicclassNoteServiceImplimplementsNoteService{@ResourceprivateSearchServicesearchService;@Transactional(rollbackFor=Throwable.class)@OverridepublicCommonResponsesave(NoteEntitynote){//一系列DB操作try{searchService.sync(注意);}catch(Exceptione){e.printStackTrace();}returnCommonResponse.success(entity);}}@Service("searchService")publicclassSearchServiceImplimplementsSearchService{@Transactional(rollbackFor=Throwable.class)@Overridepublicvoidsync(NoteEntitynote){//一系列DB操作thrownewRuntimeException("同步异常![XXX]");}}@SpringBootTestpublicclassNoteTests{@ResourceprivateNoteServicenoteService;@TestpublicvoidsaveNote(){NoteEntityentity=newNoteEntity();entity.setTitle("念奴娇赤壁乡愁");entity.setContent("大江东去,浪花冲刷,千古有风流人。老基地西边,人道是:三王朝周郎赤壁多发性硬化症...");entity.setTags("宋代苏轼");entity.setCategory("苏轼诗词");try{noteService.save(entity);}catch(Exceptione){e.printStackTrace();//FIXME我想我得到的是一个同步异常![XXX]//FIXME但我得到的是Transactionsilentlyrolledbackbecauseithasbeenmarkedasrollback-onlySystem.out.println(">>>>>>>>>>>"+e.getMessage());}}}之所以会这样,是因为代码年代久远,所以为什么要这样写,已经无从追溯了。想了一会儿,当看到double-layertransaction,想起了Spring的事务传播机制,自己理解的比较肤浅。整理了所有Spring系列面试题及答案。请关注公众号Java技术栈,回复:面试。没有特别的配置,所以自然要使用默认的事务传播机制,即Propagation.REQUIRED,国际惯例,列出事务传播机制ism:1.PROPAGATION_REQUIRED当前没有事务,创建事务;如果有交易,加入交易。这是最常用的设置。2.如果PROPAGATION_SUPPORTS中有事务,加入事务。加入交易;如果没有当前事务,将抛出异常。4.PROPAGATION_REQUIRES_NEW无条件创建新事务。5.PROPAGATION_NOT_SUPPORTED以非事务方式执行。如果有当前事务,则暂停当前事务。6.PROPAGATION_NEVER以非事务方式运行,如果有事务则抛出异常。7.在PROPAGATION_NESTED开始执行事务之前,先保存一个savepoint。当异常发生时,回滚到保存点;无异常时,与外部事务一起提交或回滚。具体原因1、看完上面的事务传播机制,继续细化问题。内外层共享一个事务,内层抛出异常,会导致整个事务失败。2、继续分析,外层逻辑有一个trycatch,会导致内层的异常没有继续往上抛,继续提交外层事务。3.事务提交时,判断事务状态,发现事务已经失败,需要回滚,于是事务静默回滚,因为已经被标记为rollback-only抛出异常。另外,如果你想学SpringBoot,看看这个仓库,太全面了。如何解决https://github.com/javastacks/spring-boot-best-practice?自然,没有灵丹妙药。根据业务场景选择合适的解决方案。1、当前场景,去掉外层逻辑中的trycatch即可。如果异常直接向上抛出,则事务不会继续提交,调用者拿到第一手的异常;2、如果内层不是核心逻辑,记录个日志什么的,可以配置内层事务为@Transactional(rollbackFor=Throwable.class,propagation=Propagation.REQUIRES_NEW),无论如何,一个新的事务是已创建,外部事务不受内部事务的影响。但有一个问题。如果外层事务失败,内层事务仍然保存着记录,可能会产生脏数据;3、如果外层事务失败,内层事务无法提交,那么可以使用@Transactional(rollbackFor=Throwable.class,propagation=Propagation.NESTED)。注意:hibernate/jpa不支持嵌套事务NESTED,可以用JdbcTemplate代替。