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

发布于 2024-10-01 13:50:36 字数 2312 浏览 3 评论 0原文

首先,我的框架是带有 JSF、托管 bean、EJB 和 JPA 的 Java EE 6。我编写了一个简单的程序来从数据库中查询信息。因此,当我单击按钮时,它会触发托管 bean 的事件,其中事件侦听器方法将访问 EJB 方法。 EJB 方法将对实体执行简单的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 永远不会在相应的 try 语句主体中抛出

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

这是我的 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"/>

<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;
}

First of all, my framework is Java EE 6 with JSF, managed bean, EJB and JPA. I write a simple program to query information from the database. So when I click a button, it trigger an event to a managed bean, where an event listener method will access EJB method. The EJB method will do a simple select query to an Entity. If the database is shutdown before or during the time I select, I get an exception

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

How do I safeguard away from this Exception? Definitely a try, catch here, but not sure where to put it. When I do em.createNamedQuery or em.remove, I try to catch com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, but I got an error said: Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement.

Below are my codes, where would I catch the Exception?

This is my EJB

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

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

This is my ManagedBean

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

   @EJB
   DocumentSBean sBean;

   List<User> users = null;

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

EDIT

I try both ways as listed below, but still return status 200 instead of 500 on firebug

<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>

Here is setDisplayFacility()

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

分分钟 2024-10-08 13:50:36

如何避免出现此异常?绝对是一次尝试,在这里抓住,但不知道把它放在哪里。

仅捕获可以以合理方式处理的异常。


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

在本例中,CommunicationsExceptionDatabaseException嵌套异常。 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.
    }
}

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


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

取决于您希望如何处理异常。如果您想将其显示在通用错误页面中,那么您不应该自己捕获它,而应该让它消失。然后 servlet 容器将自行捕获并处理它。它将在 web.xml 中查找最匹配的 并显示它。

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

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

如果您想在特定错误页面中显示它,那么您需要声明更具体的以匹配实际的异常类型。例如:

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

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

如果您希望在 PrimeFaces 收到 ajax 错误时显示一些通用 JSF UI,请使用 error 方面。例如

<p:ajaxStatus>
    <f:facet name="start"><h:graphicImage value="images/ajax-loader.gif" /></f:facet>
    <f:facet name="success"><h:outputText value="" /></f:facet>
    <f:facet name="error">
        <h:panelGroup layout="block" styleClass="ui-message-error ui-widget ui-corner-all">
            <h:outputText value="An error has occurred!" /><br />
            <h:outputLink value="#" onclick="window.location.reload(true)"><h:outputText value="Please reload page and retry" /></h:outputLink><br />
            <h:outputLink value="mailto:[email protected]?subject=Ajax%20Error"><h:outputText value="If in vain, please contact support" /></h:outputLink>
        </h:panelGroup>
    </f:facet>
</p:ajaxStatus>

(但是 PrimeFaces 2.2 RC1 中存在一些错误这会导致显示错误面失败,它在 PrimeFaces 2.1 中正常工作)

How do I safeguard away from this Exception? Definitely a try, catch here, but not sure where to put it.

Only catch the exception there where you can handle it in a reasonable manner.


I try to catch com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, but I got an error said: Exception com.mysql.jdbc.exceptions.jdbc4.CommunicationsException is never thrown in body of corresponding try statement.

The CommunicationsException is in this case a nested exception of DatabaseException. EclipseLink is under the covers already catching the CommunicationsException and rethrowing it as DatabaseException with the CommunicationsException as root cause. Something like:

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

In theory, you can only handle it as follows:

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

Which is however ugly and not recommended in this particular case.


Below are my codes, where would I catch the Exception?

Depends on how you would like to handle the exception. If you want to display it in a generic error page, then you should not catch it yourself, but just let it go. The servletcontainer will then catch and handle it itself. It will lookup the best matching <error-page> in the web.xml and display it.

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

This one will display /generic-error.xhtml for all subclasses of java.lang.Exception.

If you want to display it in a specific error page, then you need to declare the <exception-type> more specific to match the actual exception type. E.g.:

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

However, although not explicitly specified in your question, I know based on your question history that you're using JSF with PrimeFaces. You need to keep in mind that PrimeFaces will not display the error page when the initial request was made by ajax. Instead, its ajax view handler has already catched the exception itself and it will delegate to the <p:ajaxStatus> component in the view. Try adding ajax="false" to the PrimeFaces command component and you'll finally see the servletcontainer's default error page being displayed (or any of yours if a matching one in web.xml is found).

If you want to display some generic JSF UI when PrimeFaces received an ajax error, then use the error facet of <p:ajaxStatus>. E.g.

<p:ajaxStatus>
    <f:facet name="start"><h:graphicImage value="images/ajax-loader.gif" /></f:facet>
    <f:facet name="success"><h:outputText value="" /></f:facet>
    <f:facet name="error">
        <h:panelGroup layout="block" styleClass="ui-message-error ui-widget ui-corner-all">
            <h:outputText value="An error has occurred!" /><br />
            <h:outputLink value="#" onclick="window.location.reload(true)"><h:outputText value="Please reload page and retry" /></h:outputLink><br />
            <h:outputLink value="mailto:[email protected]?subject=Ajax%20Error"><h:outputText value="If in vain, please contact support" /></h:outputLink>
        </h:panelGroup>
    </f:facet>
</p:ajaxStatus>

(there's however some bug in PrimeFaces 2.2 RC1 which causes displaying the error facet to fail, it works correctly in PrimeFaces 2.1)

多情癖 2024-10-08 13:50:36

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

但是,如果您没有此选项,也不希望用户看到大异常,那么您可能会尝试做的就是在发生此特定错误时将他引导到某个用户友好的页面。为此,请将以下内容放入您的 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 块中处理它们 - 如果可能的话,通常被认为是一种关键错误的不良做法可能会被忽视。

关于您的“永远不会在相应的 try 语句主体中抛出”问题,您可能需要检查 这篇文章。

干杯!

In my view at least, a database becoming unavailable is a serious error that needs to be handled and usually ASAP. The general approach on safeguarding against such situation is enforcing high-avaialbility and failover mechanisms through database clustering.

If you don't have this option however nor you want the user to see a big-fat-exception, what you might try to do is route him in some user-friendly page when this particular error happens. To do so, put the following in your web.xml (assuming that your FacesServlet is mapped to *.faces):

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

Then, in the error.xhtml page the general approach is to put some user-friendly and largely apologetic message indicating how sorry the administrator feels for the user's inconvenience...

At any rate, dealing with such situations by catching exceptions and not acting upon them, either by re-throwing them or dealing with them in the catch block-if possible, is generally considered as a bad practice at critical errors might go un-noticed.

Regarding your "is never thrown in body of corresponding try statement" question you might want to check this article.

Cheers!

天生の放荡 2024-10-08 13:50:36

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

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

The CommunicationsException is wrapped in a EclipseLink DatabaseException, which is a runtime Exception. If you are using JPA or JTA, then this may also get wrapped in a PersistenceException, or a TransactionRolledbackException. So, try catching one of these, or worst case RuntimeException. The CommunicationsException will be in the caused by chain.

EclipseLink will automatically try to reconnect dead connections, but if your database is down, this will fail (you may see the error logged 3 times), so you will need to report an error to your user.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文