在Spring和Hibernate中,同时处理Controller、Service和DAO层的异常有哪些最佳实践?

9

我正在使用Spring 3.2和Hibernate 3.6进行工作,有人能解释一下如何在Spring MVC和Hibernate中处理异常...我只是分享一些示例代码。

控制器层

public Integer saveEployee(HttpServletRequest req, HttpServletResponse res){
    Employee empObj = new Employee();
    empObj.setName(req.getParameter("empName"));
    ......................
    ......................
    Integer empId = materService.saveEmployee(empObj);
    return empId; 
}

服务层

public Integer saveEmployee(Employee empObj){
    return masterDao.saveEmployee(empObj);
}

数据访问层

public Integer saveEmployee(Employee empObj){
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    Integer empId = session.save(empObj);
    tx.commit();
    session.close();
    return empId;
}
  1. 现在假设在保存empObj时,DAO层发生了任何异常,例如数据库崩溃、连接失败或其他类型的Hibernate异常,如ConstraintViolationExceptionIntegrityConstraintViolationException等。

  2. 如果存在Java异常(例如NullPointerException或任何用户定义的异常)需要在控制器层进行处理。

因此,如何同时在控制器、服务和DAO层处理异常是什么最佳实践或应该注意的呢?


你可以使用@ControllerAdvice来处理应用程序中任何层抛出的异常。我的意思是,如果DAO或Service层发生任何异常,它将被抛到Controller层。您的ControllerAdvice将处理每个控制器的异常。您可以参考这篇文章https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc。 - thoitbk
@thoitbk,如果在DAO层发生任何类型的Hibernate异常,比如ConstraintViolationException,这项工作还有效吗? - Naveen
当然可以。您可以根据需要处理任何类型的异常。只需在ControllerAdvice类中创建每个方法以处理每种类型的异常。这些方法用注解@ExceptionHandler(value={ConstraintViolationException.class, IntegrityConstraintViolationException.class,...})进行标记。 - thoitbk
3个回答

12

根据我的经验,在新项目中,

  1. 不应该在DAO层处理异常。 原因:通常情况下我们将@Transactional注释放置在Service层。因此,我们需要在Service层回滚事务。如果你在DAO中处理异常,则无法回滚。 请点击以下链接了解为什么应该将@Transactional放置在Service层。
    在Service层或DAO中使用"@Transactional"注释的位置应该放在哪里?
  2. 不应该在Service层处理异常。 原因:Service层执行多个DAO进行数据库操作,并且如果任何一个DAO失败,我们需要回滚事务。如果我们在Service层处理异常,则可能无法回滚事务。 虽然有手动回滚事务的方法,但不建议使用。 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

所以应该在控制器层中处理异常。


5

在应用程序的所有层级中,您不需要同时处理异常;您必须考虑其上下文含义以及适用于您的应用程序的适当策略。有些错误应该被忽略,有些应该被包装,有些应该直接引发。

spring-mvc 应用程序中处理异常的一种方法是使用适当的自定义异常来包装来自底层库的致命错误,命名为它们所抛出的层级,例如 ServiceExceptionRepositoryException。然后,@ControllerAdvice 注释的类可以使用 @ErrorHandler 注释的方法处理这些错误并返回 5XX http 错误。

常见的应用程序错误,例如由于错误的 id 导致实体未被找到,可以导致引发自定义异常,例如 NotFoundException 并在您的 @ControllerAdvice 注释的类中捕获。

这种技术的优点是,在不同的应用程序层中,您需要更少的错误处理代码,并且可以将异常转换为响应进行集中处理。

以下是一个示例 @ControllerAdvice 注释的类:

@ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler({NotFoundException.class})
    protected ResponseEntity<Object> handleNotFound(RuntimeException e, WebRequest request) {
        return handleExceptionInternal(e, e.getMessage(),
                null,
                HttpStatus.NOT_FOUND, request);
    }

    @ExceptionHandler({ServiceException.class, RepositoryException.class})
    protected ResponseEntity<Object> handleInternalError(RuntimeException e, WebRequest request) {
        return handleExceptionInternal(e, e.getMessage(),
                null,
                HttpStatus.INTERNAL_SERVER_ERROR, request);
    }
}

如果我想在异常发生时执行一些逻辑,比如将状态更改为“失败”,该怎么办? - Hitesh Kumar

0
你应该将你的服务设置为事务性,并在控制器层处理异常:你可以选择基于控制器的异常处理(使用@ExceptionHandler)或全局异常处理(使用@ControllerAdvice类)。 在出现异常情况下,向用户显示一些实用的信息,例如错误页面,可能是很实际的。
你可以在Spring MVC 这里找到有用的异常处理示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接