如何从事务性 Spring 服务中抛出自定义异常



我有这个春季服务:

@Service
@Transactional
public class ConsorcioServiceImpl implements ConsorcioService {
    ...
    @Autowired
    private ConsorcioRepository consorcioRepository;
    @Override
    public void saveBank(Consorcio consorcio) throws BusinessException {
        try {
            consorcioRepository.save(consorcio);
        }
        catch(DataIntegrityViolationException divex) {
            if(divex.getMessage().contains("uq_codigo")) {
                throw new DuplicatedCodeException(divex);
            }
            else {
                throw new BusinessException(dives); 
            }
        }
        catch (Exception e) {
            throw new BusinessException(e);
        }
    }

}

该服务使用此 Spring 数据存储库:

@Repository
public interface ConsorcioRepository extendsCrudRepository<Consorcio, Integer> {

}

我正在从弹簧控制器调用服务:

@Controller
@RequestMapping(value = "/bank")
public class BancaController {
    @Autowired
    private ConsorcioService consorcioService;
    @RequestMapping(value="create", method=RequestMethod.POST)
    public ModelAndView crearBanca(@Valid BancaViewModel bancaViewModel, BindingResult bindingResult,
                                   RedirectAttributes redirectAttributes) {
        ModelAndView modelAndView;
        MessageViewModel result;
        try {
            consorcioService.saveBank(bancaViewModel.buildBanca());
            result = new MessageViewModel(MessageType.SUCESS);
            redirectAttributes.addFlashAttribute("messageViewModel", result);
            modelAndView = new ModelAndView("redirect:/banca/crear");
            return modelAndView;
        } catch (Exception e) {
            result = new MessageViewModel(MessageType.ERROR);
            modelAndView = new ModelAndView("crear-bancas");
            modelAndView.addObject("messageViewModel", result);
            return modelAndView;
        }
}

但我在控制器中得到的例外是:org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly而不是我在服务中投入的DuplicatedCodeException。我需要确定异常的类型,以便我可以提供自定义友好的用户消息。

你的DuplicatedCodeException,BusinessException应该是运行时异常,或者为方法saveBank添加:

@Transactinal(rolbackFor={BusinessException.class,DuplicatedCodeException.,class }(

在其他情况下,Spring 不会回滚事务。

来自 Spring 文档:

虽然 EJB 缺省行为是针对 EJB 容器 在系统异常时自动回滚事务(通常 运行时异常(,EJB CMT 不会回滚事务 在应用程序异常(即选中 java.rmi.RemoteException以外的异常(。虽然春天 声明式事务管理的缺省行为遵循 EJB 约定(仅在未选中的异常时自动回滚(,它 通常可用于自定义此内容。

只需在分支之前添加catch (TransactionSystemException tse) catch (Exception e)然后使用getOriginalException()提取异常。

try {
    consorcioService.saveBank(bancaViewModel.buildBanca());
    result = new MessageViewModel(MessageType.SUCESS);
    redirectAttributes.addFlashAttribute("messageViewModel", result);
    modelAndView = new ModelAndView("redirect:/banca/crear");
    return modelAndView;
} catch (TransactionSystemException tse) {
    final Throwable ex = tse.getOriginalException();
    if (ex instanceof DuplicatedCodeException) {
        // DuplicatedCodeException
    }
} catch (Exception e) {
    result = new MessageViewModel(MessageType.ERROR);
    modelAndView = new ModelAndView("crear-bancas");
    modelAndView.addObject("messageViewModel", result);
    return modelAndView;
}

发生这种情况是因为您的异常被包装在RollbackExceptionThrowable原因。反过来RollbackException也是TransactionSystemException的原因。

您可以构建全局异常处理程序来根据需要捕获和自定义所有异常:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.CONFLICT) 
    @ExceptionHandler(TransactionSystemException.class)
    public ModelAndView handleDupilatedCode(HttpServletRequest req, TransactionSystemException ex) {
        // Build you exception body here
        Throwable e = ex.getOriginalException();
        if(e instanceof DuplicatedCodeException)
          // throw
        // Or build custom exception as you want
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName("error");
        return mav;
    }
}

@ControllerAdvice 自 Spring 3.2 起可用

您也可以在@Controller级别使用 @ExceptionHandler,或者在抽象控制器中创建此处理(如果您将抽象控制器作为所有控制器的超类(。

public class FooController {
    //...
    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public void handleException() {
        //
    }
}

还有其他一些方法,为了完整参考,请遵循:

Spring

IO:Spring MVC 中的异常处理

Baeldung:使用弹簧进行异常处理

最新更新