JavaEE6:数据库关闭时如何保护Web应用程序
首先,我的框架是带有 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.createNamedQuery
或 em.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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
仅捕获可以以合理方式处理的异常。
在本例中,
CommunicationsException
是DatabaseException
的嵌套异常。 EclipseLink 已经在幕后捕获了CommunicationsException
并将其作为DatabaseException
重新抛出,并将CommunicationsException
作为根本原因。类似这样的:理论上,你只能按如下方式处理它:
然而这很丑陋,在这种特殊情况下不推荐。
取决于您希望如何处理异常。如果您想将其显示在通用错误页面中,那么您不应该自己捕获它,而应该让它消失。然后 servlet 容器将自行捕获并处理它。它将在
web.xml
中查找最匹配的
并显示它。这个将显示
java.lang.Exception
的所有 子类的/generic-error.xhtml
。如果您想在特定错误页面中显示它,那么您需要声明更具体的
以匹配实际的异常类型。例如:但是,尽管您的问题中没有明确指定,但根据您的问题历史记录,我知道您正在使用 JSF 和 PrimeFaces 。您需要记住,当 ajax 发出初始请求时,PrimeFaces 将不会显示错误页面。相反,它的 ajax 视图处理程序本身已经捕获了异常,并将委托给
视图中的
组件。尝试将ajax="false"
添加到 PrimeFaces 命令组件,您最终将看到显示 servletcontainer 的默认错误页面(或者您的任何页面,如果web.xml
中有匹配的错误页面)代码> 已找到)。如果您希望在 PrimeFaces 收到 ajax 错误时显示一些通用 JSF UI,请使用
的error
方面。例如(但是 PrimeFaces 2.2 RC1 中存在一些错误这会导致显示错误面失败,它在 PrimeFaces 2.1 中正常工作)
Only catch the exception there where you can handle it in a reasonable manner.
The
CommunicationsException
is in this case a nested exception ofDatabaseException
. EclipseLink is under the covers already catching theCommunicationsException
and rethrowing it asDatabaseException
with theCommunicationsException
as root cause. Something like:In theory, you can only handle it as follows:
Which is however ugly and not recommended in this particular case.
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 theweb.xml
and display it.This one will display
/generic-error.xhtml
for all subclasses ofjava.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.: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 addingajax="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 inweb.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.(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)
至少在我看来,数据库变得不可用是一个严重的错误,需要尽快处理。防止这种情况的一般方法是通过数据库集群实施高可用性和故障转移机制。
但是,如果您没有此选项,也不希望用户看到大异常,那么您可能会尝试做的就是在发生此特定错误时将他引导到某个用户友好的页面。为此,请将以下内容放入您的 web.xml 中(假设您的 FacesServlet 映射到 *.faces):
然后,在 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):
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!
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.