Spring 内部事务调用导致事务实效解决方法

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;

   @Override
    public Result<List<OutboundApplyNoteDetailIdDTO>> insertOutboundApplyNote(CreateOutboundApplyNoteDTO dto) {
        List<OutboundApplyNoteDetailIdDTO> data = outboundApplyNoteService.insertOutboundApplyNote(dto);
        return ok(data);
    }
    
    @Override
    public 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事务未生效的情况。
  
 
posted @ 2024-12-27 15:59  currentTimeMillis  阅读(1)  评论(0编辑  收藏  举报