使用Spring/JPA/JSF的异常处理策略

7
我们的应用程序使用JSF、Spring和JPA技术。我们正在努力简化项目中的异常处理策略。
我们的应用程序结构如下:
UI(JSF) --> 管理的Bean --> 服务 --> DAO
我们在DAO层使用异常转换bean后处理器。这是在Spring应用程序上下文文件中配置的。
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> 

Spring将所有数据库异常封装到“org.springframework.dao.DataAccessException”中。我们在DAO层不进行其他异常处理。

我们处理以下异常的策略如下:

表示层:

Class PresentationManangedBean{

 try{
      serviceMethod();
   }catch(BusinessException be){
      // Mapping exception messages to show on UI
   }
   catch(Exception e){
       // Mapping exception messages to show on UI
   }

}

服务层

@Component("service")
Class Service{

 @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = BusinessException.class)
 public serviceMethod(){

  try{

      daoMethod();

   }catch(DataAccessException cdae){
      throws new BusinessException(); // Our Business/Custom exception
   }
   catch(Exception e){
      throws new BusinessException();  //  Our Business/Custom exception
   }
 }

}

DAO Layer

@Repository("dao")
Class DAO{

 public daoMethod(){
  // No exception is handled
  // If any DataAccessException or RuntimeException is occurred this 
  // is thrown to ServiceLayer
 }

}

问题: 我们只想确认上述方法是否符合最佳实践。如果不是,请建议我们处理异常(与事务管理相结合)的最佳方式是什么?

答案: 我们只是想确认上述方法是否符合最佳实践。如果不是,建议您采用什么最佳方式来处理异常(与事务管理相结合)。

我也想采用这种方法,但是当我捕获异常并抛出我们自定义的异常时,我的异常处理程序无法捕获该异常。Spring将我的异常包装成 org.springframework.transaction.TransactionSystemException。这是我的问题 http://stackoverflow.com/questions/28295684/unable-to-wrap-dao-exception-in-service-layer-using-spring-mvc。我该如何解决这个问题? - Harmeet Singh Taara
5个回答

1
这种方法看起来很不错。我们在项目中也使用了同样的方法。
Vinay

1
我采用了一种不同的方法:
- 我不在DAO中捕获Spring的DAE。我让它们流到控制器(JSF管理的bean)中。
- 在控制器中,我使用一个“catch”来捕获任何异常。
- 我调用一个自定义的“handleException”方法,该方法接收在参数中捕获的异常。
- 这个异常处理程序检查(例如,使用“if-then-else”语句),参数是什么类型的异常,并根据该异常的类型向用户显示消息。在我的情况下,我在属性文件中查找要显示的消息,其中消息的键(以及任何参数)是异常的属性(当我抛出异常时将它们放在那里)。特别是,如果您想以特殊方式处理DAE,则可以在此异常处理程序方法中放置一个“if”分支,并对其进行任何操作。
我认为这种方法更好、更清洁,因为您只需要在一个点上处理异常,并且不必在每个级别上都放置太多的“throws-try-catch”,只需在控制器(JSF)中即可。
当然,如果你想在特定情况下执行一些业务逻辑而不是向用户显示消息,你可以在服务中使用“try-catch”处理特定异常。
此外,如果你不想在控制器中处理Spring DAE,可以将其包装成自己的业务异常并重新抛出。但请在服务层而非DAO中执行此操作。
希望这个答案能够帮到你。

0

我自己也有些困惑,所以进行了一些研究。不过我的看法略有不同。

理由是,在多个层级上抛出和捕获异常在性能和代码可读性方面都很昂贵。关键在于服务方法不应该抛出异常。相反,它们应该始终返回一个响应,该响应应该封装状态、消息和输出实体。

但是,如果控制器调用多个服务方法并尝试根据响应组合进行绘制,则可以将其放入 try catch 块中并处理异常。

按照这种逻辑,它可以平等地处理 sprint mvc 和 rest mvc。请让我知道您的想法。

Class PresentationManangedBean{
      try {
      Response resp1 = serviceMethod();
      if (resp1.getStatus().equals("SUCCESSFUL"))
           // handle UI painting logic.
      else
           // handle error for UI
      Response resp2 = serviceMethod2();
      if (resp.getStatus().equals("SUCCESSFUL"))
           // handle UI painting logic.
      else
           // handle error for UI
      // handle resp1 and resp2 to paint UI.  
     } catch (Exception e) {
           // handle error for UI 
     }
}

1)服务应该处理所有的异常。控制器不应该处理任何异常。 2)


0
也许你可以将 BusinessException 和 BusinessRuntimeException 分开处理,因为当你捕获 BusinessException 时,抛出的是 BusinessException,但当你捕获 Exception 时,仍然抛出同样的 BusinessException。尝试将它们分开处理。顺便问一下,为什么在捕获 BusinessException 时要抛出相同的异常呢?

谢谢您的评论Erhan,我会看一下并回复您。 - Narendra Verma

0

你的方法非常符合Spring框架的建议,也避免了在应用程序的各个层之间传递大量的已检查异常。


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