Spring 事务,当方法内部调用的时候, 事务会失效。
/** * 在事务A方法中,直接调用事务B方法,B方法拥有事务的能力是因为 spring aop 生成了代理对象,但是方法直接调用了this对象的方法,所以B方法不会生成事务。 * 这里的@Transactional 将会失效 */ @Transactional public void edit(User user) {editMsgByUser(user);// ....... }@Transactional public void editMsgByUser(User user) {// 这里的事务如果会滚,当前这个方法的事务就会失效。 }
如果看上面这个例子,即使B事务失效,A事务也会出发会滚。 也能做到会滚的效果。
但是实际应用中,遇到一种场景。
/** * 这是批量新增的接口, 接口要求, 批量进增时,新增成功返回成功数据, 失败的返回异常数据。 */ public List<OutboundApplyNotesDTO> insertOutboundApplyNoteList(List<CreateOutboundApplyNoteDTO> dto) {List<OutboundApplyNotesDTO> dataList = Lists.newArrayList();for(CreateOutboundApplyNoteDTO createOutboundApplyNoteDTO : dto) { try {List<OutboundApplyNoteDetailIdDTO> applyNoteDetailids = insertOutboundApplyNote(createOutboundApplyNoteDTO);OutboundApplyNotesDTO applyNotesDTO = OutboundApplyNotesDTO.builder().insertCode(OutboundResponseEnum.SUCCESS.getCode()).insertMsg(OutboundResponseEnum.SUCCESS.getMessage()).build();dataList.add(applyNotesDTO);} catch (Exception e) {log.error("新增出库申请单异常:", e);OutboundApplyNotesDTO applyNotesDTO = OutboundApplyNotesDTO.builder().insertCode(OutboundResponseEnum.ADD_ERROR.getCode()).insertMsg(e.getMessage()).build();dataList.add(applyNotesDTO);}}return dataList; }/** * 这里进行单个单据的插入, 如果插入中有异常抛出,数据需要会滚。 */ @Transactional(rollbackFor = Exception.class) public List<OutboundApplyNoteDetailIdDTO> insertOutboundApplyNote(CreateOutboundApplyNoteDTO dto) {// throw new BusinessException(OutboundResponseEnum.ORDER_EXIST, valNote.getOutboundApplyNoteNo());return new List(); }
在上面这个接口中, 接口A 不允许抛出异常,B接口如果有异常,需要回滚。 但是此时B方法中事务没有生效,就会造成尴尬。
当Spring容器启动时候, 发现有 @EnableTransactionManagement 注解,会拦截所有bean的创建,扫描bean上是否有 @Transaction 注解,如果有这个注解,spring 会通过 aop方法给这个bean生成代理对象(代理对象存在本类对象),代理对象中会增加一个拦截器,拦截器会拦截bean中public方法的执行,在方法执行前启动事务,方法执行完毕之后提交或者回滚事务。
事务生效:必须通过代理对象来调用方法。代理对象最终都是要调用原始对象的,而原始对象去调用方法时,是不会再出发代理了。
所以同一个类中, A调用 B, B方法上的事务不会单独生效。
此时又遇到了,上面这种业务场景。 就需要考虑,把这两个方法,放到不同的类中去。
/** * 对外提供的Controller 方法, * 里面有两个方法, 一个单个新增, 一个批量新增。 */ @RestController @Api(value = "出库申请单相关") @RequiredArgsConstructor public class OutboundApplyController implements IWmsController, OutboundApplyNoteApiService {// 这里分开两个service方法 private final OutboundApplyNoteService outboundApplyNoteService;private final OutboundApplyNoteDetailService outboundApplyNoteDetailService;@Overridepublic Result<List<OutboundApplyNoteDetailIdDTO>> insertOutboundApplyNote(CreateOutboundApplyNoteDTO dto) {List<OutboundApplyNoteDetailIdDTO> data = outboundApplyNoteService.insertOutboundApplyNote(dto);return ok(data);}@Overridepublic Result<List<OutboundApplyNotesDTO>> insertOutboundApplyNoteList(List<CreateOutboundApplyNoteDTO> dto) {List<OutboundApplyNotesDTO> data = outboundApplyNoteDetailService.insertOutboundApplyNoteList(dto);return ok(data);}}/** * A 类, A方法 */ @Slf4j @Service @RequiredArgsConstructor public class OutboundApplyNoteDetailServiceImpl extends WmsService implements OutboundApplyNoteDetailService {private final OutboundApplyNoteService outboundApplyNoteService;/** * 这是批量新增的接口, 接口要求, 批量进增时,新增成功返回成功数据, 失败的返回异常数据。 */public List<OutboundApplyNotesDTO> insertOutboundApplyNoteList(List<CreateOutboundApplyNoteDTO> dto) {List<OutboundApplyNotesDTO> dataList = Lists.newArrayList();for(CreateOutboundApplyNoteDTO createOutboundApplyNoteDTO : dto) { try {List<OutboundApplyNoteDetailIdDTO> applyNoteDetailids = outboundApplyNoteService.insertOutboundApplyNote(createOutboundApplyNoteDTO);OutboundApplyNotesDTO applyNotesDTO = OutboundApplyNotesDTO.builder().insertCode(OutboundResponseEnum.SUCCESS.getCode()).insertMsg(OutboundResponseEnum.SUCCESS.getMessage()).build();dataList.add(applyNotesDTO);} catch (Exception e) {log.error("新增出库申请单异常:", e);OutboundApplyNotesDTO applyNotesDTO = OutboundApplyNotesDTO.builder().insertCode(OutboundResponseEnum.ADD_ERROR.getCode()).insertMsg(e.getMessage()).build();dataList.add(applyNotesDTO);}}return dataList;} }/** * B 类 B方法 */ @Slf4j @Service @RequiredArgsConstructor public class OutboundApplyNoteServiceImpl extends WmsService implements OutboundApplyNoteService {/*** 这里进行单个单据的插入, 如果插入中有异常抛出,数据需要会滚。 */@Transactional(rollbackFor = Exception.class)public List<OutboundApplyNoteDetailIdDTO> insertOutboundApplyNote(CreateOutboundApplyNoteDTO dto) {// throw new BusinessException(OutboundResponseEnum.ORDER_EXIST, valNote.getOutboundApplyNoteNo());return new List();} }
这样就可以解决遇到的B事务未生效的情况。