JPA在ejb容器中引发了哪些异常?

最后发布: 2010-03-07 02:00:11


问题

我正在开发一个可以在glassfish v3上运行的EJB应用程序。 如果我查看EntityManager类的javadoc,它会说诸如find,persist等方法抛出从PersistenceException派生的异常。 但是,在实践中,我注意到,如果在数据库级别出现问题(例如,找不到表),则可能引发从org.eclipse.persistence.exceptions.DatabaseException派生的异常。 在假设除了标准持久性异常之外,我还必须处理我使用的任何持久性提供程序引发的异常,对吗? 这往往意味着我需要编写特定于我选择的JPA提供程序的错误代码,如果以后更改为其他代码,则需要更改代码以捕获其他异常类,例如HibernateException。

java jpa java-ee ejb-3.0
回答

我认为,您仅应处理标准JPA异常层次结构中的异常(除非您要处理规范没有标准异常的特定情况,否则您的应用程序将无法移植 -但我可以别想了) EJB 3.0 JPA规范(JSR 220)在3.7节中对它们进行了总结:

3.7例外摘要

以下是本规范定义的例外的摘要:

PersistenceException

发生问题时,持久性提供程序将引发PersistenceException 可能会报告由于意外错误(例如,持久性提供程序无法打开数据库连接)而导致调用的操作无法完成。
本规范定义的所有其他异常是PersistenceException子类。 NoResultExceptionNonUniqueResultException实例外,所有PersistenceException实例都将导致当前事务(如果活动的话)被标记为回滚。

TransactionRequiredException

当需要事务但未激活事务时,持久性提供程序将抛出TransactionRequiredException。

OptimisticLockException

当发生乐观锁定冲突时,持久性提供程序将引发OptimisticLockException 可能在刷新或提交时将异常作为API调用的一部分抛出。 如果当前事务处于活动状态,则将其标记为回滚。

RollbackException

EntityTransaction.commit失败时,持久性提供程序将抛出RollbackException

EntityExistsException

当持久化调用被调用并且实体已经存在时,持久化提供者可能会抛出EntityExistsException 所述EntityExistsException被调用persist操作时可能抛出,或EntityExistsException或另一个PersistenceException可以在提交时被抛出。

EntityNotFoundException

当访问通过getReference获得的实体引用但该实体不存在时,持久性提供程序将抛出EntityNotFoundException 当实体不再存在于数据库中时,刷新操作也会抛出该错误。 如果当前事务处于活动状态,则将其标记为回滚。

NoResultException

NoResultException是由持久性提供时抛出Query.getSingleResult被调用,并且没有结果返回。 如果当前事务处于活动状态,则此异常不会导致当前事务被标记为回滚。

NonUniqueResultException

调用Query.getSingleResult时,持久性提供程序将抛出NonUniqueResultException并且该查询有多个结果。 如果当前事务处于活动状态,则此异常不会导致当前事务被标记为回滚。

对我来说,特定于提供程序的异常通常是“内部”内容,用于表示技术问题,即应修复的应用程序中的错误(例如,如果表丢失,是错误,请对其进行修复,这是没有意义的)处理此类异常)。


回答

我对此做了更多的实验。 当我切换到Hibernate时,我发现如果我在调用EntityManager方法作为Throwable的bean中捕获到异常,那么如果发生了PersistenceException子类所支持的情况之外的事情(例如缺少表),则HibernateException被包装在一个普通的Persistence Exception中,这很有意义。 如果您想知道持久性是否出了问题,可以捕获PersistenceException。

在TopLink情况下,我直接收到Eclipse DatabaseException。 在我看来,这似乎是个错误-正如Pascal所提到的,应该不需要捕获提供程序特定的异常。 我将为Glassfish提交一个错误报告,并在此处发布指向结果的链接。

在这两种情况下,如果未在EntityManager调用的站点上捕获到异常,则该异常将被更通用的容器(例如EJBException或TransactionRolledBackException)捕获并重新抛出,在大多数情况下捕获这些异常可能更有意义。


回答

这就是为什么Spring框架将特定于实现的异常转换为独立于实现的异常的原因。 如果您担心捕获“内部”异常类型的耦合,则可以在持久层中编写异常代理代码以捕获DatabaseException并重新抛出您自己的ShanesPersistenceException extends RuntimeException (或其他任何原因),并将原始异常包括在内。 然后,如果以后再切换实现,则只需触摸异常代理即可为新供应商的异常添加捕获并重新抛出。