当容器管理的 tx EJB 提交时,如何捕获和包装 JTA 抛出的异常? [英] How to catch and wrap exceptions thrown by JTA when a container-managed-tx EJB commits?

查看:17
本文介绍了当容器管理的 tx EJB 提交时,如何捕获和包装 JTA 抛出的异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力解决管理非平凡数据模型的 EJB3 类的问题.当我的容器管理的事务方法提交时,我抛出了约束验证异常.我想防止它们被包裹在 EJBException 中,而不是抛出一个调用者可以处理的正常应用程序异常.

I'm struggling with a problem with an EJB3 class that manages a non-trivial data model. I have constraint validation exceptions being thrown when my container-managed transactional methods commit. I want to prevent them from being wrapped in EJBException, instead throwing a sane application exception that callers can handle.

要将其包装在合适的应用程序异常中,我必须能够捕获它.大多数情况下,一个简单的 try/catch 就可以完成这项工作,因为验证异常是从我所做的 EntityManager 调用中抛出的.

To wrap it in a suitable application exception, I must be able to catch it. Most of the time a simple try/catch does the job because the validation exception is thrown from an EntityManager call I've made.

不幸的是,一些约束只在提交时检查.例如,映射集合上的 @Size(min=1) 违规仅在容器管理的事务提交时被捕获,一旦它在我的事务方法结束时离开我的控制.我无法捕获验证失败时抛出的异常并将其包装起来,因此容器将其包装在 javax.transaction.RollbackException 中并将 that 包装在被诅咒的 中EJB 异常.调用者必须捕获所有 EJBException 并深入原因链以试图找出它是否是验证问题,这真的不好.

Unfortunately, some constraints are only checked at commit time. For example, violation of @Size(min=1) on a mapped collection is only caught when the container managed transaction commits, once it leaves my control at the end of my transactional method. I can't catch the exception thrown when validation fails and wrap it, so the container wraps it in a javax.transaction.RollbackException and wraps that in a cursed EJBException. The caller has to catch all EJBExceptions and go diving in the cause chain to try to find out if it's a validation issue, which is really not nice.

我正在处理容器管理的事务,所以我的 EJB 看起来像这样:

I'm working with container managed transactions, so my EJB looks like this:

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER
class TheEJB {

    @Inject private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public methodOfInterest() throws AppValidationException {
       try {
           // For demonstration's sake create a situation that'll cause validation to
           // fail at commit-time here, like
           someEntity.getCollectionWithMinSize1().removeAll();
           em.merge(someEntity);
       } catch (ValidationException ex) {
           // Won't catch violations of @Size on collections or other
           // commit-time only validation exceptions
           throw new AppValidationException(ex);
       }
    }

}

... 其中 AppValidationException 是已检查异常或注释了 @ApplicationException 的未检查异常,因此它不会被 EJB3 包装.

... where AppValidationException is a checked exception or an unchecked exception annotated @ApplicationException so it doesn't get wrapped by EJB3.

有时我可以使用 EntityManager.flush() 触发早期约束违规并捕获它,但并非总是如此.即便如此,我也真的希望能够在提交时捕获由延迟约束检查引发的数据库级约束违规,而这些只会在 JTA 时永远出现提交.

Sometimes I can trigger an early constraint violation with an EntityManager.flush() and catch that, but not always. Even then, I'd really like to be able to trap database-level constraint violations thrown by deferred constraint checks at commit time too, and those will only ever arise when JTA commits.

帮助?

已经尝试过:

Bean 管理的事务 将通过允许我在我控制的代码中触发提交来解决我的问题.不幸的是,它们不是一个选项,因为 bean 管理的事务不提供任何等效的 TransactionAttributeType.REQUIRES_NEW - 无法使用 BMT 挂起事务.JTA 令人讨厌的疏忽之一.

Bean managed transactions would solve my problem by allowing me to trigger the commit within code I control. Unfortunately they aren't an option because bean managed transactions don't offer any equivalent of TransactionAttributeType.REQUIRES_NEW - there's no way to suspend a transaction using BMT. One of the annoying oversights of JTA.

见:

  • Why we need JTA 2.0
  • Bean-Managed Transaction Suspension in J2EE (don't do this!)

...但请参阅警告和详细信息的答案.

... but see answers for caveats and details.

javax.validation.ValidationException 是 JDK 异常;我无法修改它以添加@ApplicationException 注释 以防止包装.我不能子类化它来添加注释;它是由 EclpiseLink 抛出的,而不是我的代码.我不确定标记它 @ApplicationException 是否会阻止 Arjuna(AS7 的 JTA impl)将它包装在 RollbackException 中.

javax.validation.ValidationException is a JDK exception; I can't modify it to add an @ApplicationException annotation to prevent wrapping. I can't subclass it to add the annotation; it's thrown by EclpiseLink, not my code. I'm not sure that marking it @ApplicationException would stop Arjuna (AS7's JTA impl) wrapping it in a RollbackException anyway.

我尝试像这样使用 EJB3 拦截器:

@AroundInvoke
protected Object exceptionFilter(InvocationContext ctx) throws Exception {
    try {
        return ctx.proceed();
    } catch (ValidationException ex) {
        throw new SomeAppException(ex);
    }
}

...但似乎拦截器在内部触发 JTA(这是明智的并且通常是可取的)所以我想要捕获的异常还没有被抛出.

... but it appears that interceptors fire inside JTA (which is sensible and usually desirable) so the exception I want to catch hasn't been thrown yet.

我想我想要的是能够定义一个异常过滤器,该过滤器在之后应用 JTA 做它的事情.有什么想法吗?

I guess what I want is to be able to define an exception filter that's applied after JTA does its thing. Any ideas?

我正在使用 JBoss AS 7.1.1.Final 和 EclipseLink 2.4.0.根据这些说明,EclipseLink 作为 JBoss 模块安装,但这对手头的问题.

I'm working with JBoss AS 7.1.1.Final and EclipseLink 2.4.0. EclipseLink is installed as a JBoss module as per these instructions, but that doesn't matter much for the issue at hand.

更新:在对这个问题进行更多思考之后,我意识到除了 JSR330 验证异常之外,我真的还需要能够捕获 SQLIntegrityConstraintViolationException 来自数据库和 死锁或序列化失败回滚分别为 SQLSTATE 40P01 和 40001.这就是为什么仅仅试图确保提交永远不会抛出的方法行不通的原因.不能通过 JTA 提交抛出已检查的应用程序异常,因为 JTA 接口自然不会声明它们,但未检查的 @ApplicationException 注释异常应该可以.

UPDATE: After more thought on this issue, I've realised that in addition to JSR330 validation exceptions, I really also need to be able to trap SQLIntegrityConstraintViolationException from the DB and deadlock or serialization failure rollbacks with SQLSTATE 40P01 and 40001 respectively. That's why an approach that just tries to make sure commit will never throw won't work well. Checked application exceptions can't be thrown through a JTA commit because the JTA interfaces naturally don't declare them, but unchecked @ApplicationException annotated exceptions should be able to be.

似乎在任何我可以有用地捕获应用程序异常的地方,我也可以——尽管不太漂亮——捕获 EJBException 并深入研究 JTA 异常和底层验证或 JDBC 异常,然后基于此做出决策.如果没有 JTA 中的异常过滤器功能,我可能不得不这样做.

It seems that anywhere I can usefully catch an application exception I can also - albeit less prettily - catch an EJBException and delve inside it for the JTA exception and the underlying validation or JDBC exception, then do decision making based on that. Without an exception filter feature in JTA I'll probably have to.

推荐答案

这个我没试过.但我猜这应该可行.

I haven't tried this. But I am guessing this should work.

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
class TheEJB {

    @Inject
    private TheEJB self;

    @Inject private EntityManager em;

    public methodOfInterest() throws AppValidationException {
       try {
           self.methodOfInterestImpl();
       } catch (ValidationException ex) {
           throw new AppValidationException(ex);
       }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public methodOfInterestImpl() throws AppValidationException {
        someEntity.getCollectionWithMinSize1().removeAll();
        em.merge(someEntity);
    }    
}

容器应该启动一个新事务并在methodOfInterest提交,因此您应该能够在包装器方法中捕获异常.

The container is expected to start a new transaction and commit within the methodOfInterest, therefore you should be able to catch the exception in the wrapper method.

Ps:根据@LairdNelson 提供的优雅想法更新了答案...

Ps: The answer is updated based on the elegant idea provided by @LairdNelson...

这篇关于当容器管理的 tx EJB 提交时,如何捕获和包装 JTA 抛出的异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆