JavaEE6:如何在数据库关闭时保护Web应用程序

时间:2010-11-10 16:17:53

标签: jsf jpa exception-handling java-ee communicationexception

首先,我的框架是带有JSF,托管bean,EJB和JPA的Java EE 6。我编写了一个简单的程序来查询数据库中的信息。因此,当我单击一个按钮时,它会触发一个事件到托管bean,其中事件侦听器方法将访问EJB方法。 EJB方法将对实体执行简单的select查询。如果数据库在I select之前或期间关闭,我会收到异常

Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 51,460 milliseconds ago.  The last packet sent successfully to the server was 0 milliseconds ago.
Error Code: 0

如何防范此异常?这里肯定是try, catch,但不知道放在哪里。当我em.createNamedQueryem.remove时,我会尝试抓住com.mysql.jdbc.exceptions.jdbc4.CommunicationsException,但我收到了一条错误:Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement

以下是我的代码,我会在哪里捕获异常?

这是我的EJB

@Stateless
@LocalBean
public class DocumentSBean {
    @PersistenceContext
    private EntityManager em;

    public List<User> listUser(){
         Query query = em.createNamedQuery("User.listUser");
         query.returnResultList();
    }
}

这是我的ManagedBean

@ManagedBean(name="document")
@ViewScoped
public class DisplayListController implements Serializable {            

   @EJB
   DocumentSBean sBean;

   List<User> users = null;

   public void foo(){
       users = sBean.listUser();
   }
}

修改

我尝试了下面列出的两种方式,但仍然在firebug上返回状态200而不是500

<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities"/>

OR

<p:commandButton update="myform" actionListener="#{document.setDisplayFacility}" rendered="#{utility.admin}" value="Facilities">
       <p:ajax actionListener="#{document.setDisplayFacility}" update="myform" event="click"/>
</p:commandButton>

这是setDisplayFacility()

public void setDisplayFacility(){
   facilities = sBean.getAllFacilities(); //sBean is EJB
   displayFacility = true;
}

3 个答案:

答案 0 :(得分:6)

  

如何防范此异常?绝对是一个尝试,抓住这里,但不知道在哪里放。

只能在那里以合理的方式处理异常。


  

我尝试捕获com.mysql.jdbc.exceptions.jdbc4.CommunicationsException,但是我收到一条错误说:异常com.mysql.jdbc.exceptions.jdbc4.CommunicationsException永远不会在相应的try语句的主体中抛出。

CommunicationsException在这种情况下是DatabaseException嵌套例外。 EclipseLink已经覆盖CommunicationsException,并以DatabaseException为根本原因重新抛出CommunicationsException。类似的东西:

try {
     // Execute query.
} catch (Exception e) {
     throw new DatabaseException("Internal Exception: " + e, e);
}

理论上,只能按如下方式处理:

try {
     // Let EclipseLink execute query.
} catch (DatabaseException e) {
    if (e.getCause() instanceof CommunicationsException) {
        // Handle.
    }
}

然而,这种情况很丑陋,在这种特殊情况下不推荐使用。


  

以下是我的代码,我会在哪里捕获异常?

取决于您希望处理异常的方式。如果您想在通用错误页面中显示它,那么您不应该自己捕获它,而只是放手。然后servletcontainer将自己捕获并处理它。它会在<error-page>中查找最匹配的web.xml并显示它。

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/generic-error.xhtml</location>
</error-page>

这个会显示/generic-error.xhtml 所有java.lang.Exception子类。

如果要在特定的错误页面中显示它,则需要声明<exception-type>更具体,以匹配实际的异常类型。 E.g:

<error-page>
    <exception-type>org.eclipse.persistence.exceptions.DatabaseException</exception-type>
    <location>/database-error.xhtml</location>
</error-page>

但是,虽然未在您的问题中明确指出,但我根据您的问题历史知道您正在使用PrimeFaces的JSF。您需要记住,当ajax发出初始请求时,PrimeFaces将显示错误页面。相反,它的ajax视图处理程序已经捕获了异常本身,它将委托给视图中的<p:ajaxStatus>组件。尝试将ajax="false"添加到PrimeFaces命令组件中,您最终将看到正在显示的servletcontainer的默认错误页面(如果找到web.xml中匹配的错误页面,则显示任何错误页面)。

如果要在PrimeFaces收到ajax错误时显示一些通用的JSF UI,请使用error的{​​{1}}方面。 E.g。

<p:ajaxStatus>

(PrimeFaces 2.2 RC1中有一些bug导致显示错误方面失败,它在PrimeFaces 2.1中正常工作

答案 1 :(得分:2)

至少在我看来,数据库变得不可用是一个严重的错误,需要处理并且通常是尽快的。保护这种情况的一般方法是通过数据库集群实施高可用性和故障转移机制。

如果你没有这个选项,也不希望用户看到一个大胖子异常,你可能会尝试做的是在发生这个特定错误时将他路由到一些用户友好的页面。为此,请将以下内容放在web.xml中(假设您的FacesServlet映射到* .faces):

<error-page>
    <exception-type>com.mysql.jdbc.exceptions.jdbc4.CommunicationsException</exception-type>
    <location>/errors/error.faces</location>
</error-page>

然后,在error.xhtml页面中,一般方法是放置一些用户友好且很大程度上抱歉的消息,表明管理员对用户的不便感到遗憾......

无论如何,通过捕获异常并且不对它们采取行动来处理这种情况,或者通过重新抛出它们或者在catch块中处理它们 - 如果可能的话,通常被视为关键错误的坏习惯未注意到。

关于您的“永远不会在相应的试用声明中提出”问题,您可能需要查看this文章。

干杯!

答案 2 :(得分:2)

CommunicationsException包含在EclipseLink DatabaseException中,这是一个运行时异常。如果您使用的是JPA或JTA,那么这也可能包含在PersistenceException或TransactionRolledbackException中。因此,尝试捕获其中一个或最坏的情况RuntimeException。 CommunicationsException将由链引起。

EclipseLink将自动尝试重新连接死连接,但如果数据库关闭,则会失败(您可能会看到错误记录3次),因此您需要向用户报告错误。