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

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

问题描述

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



为了将它包装在一个合适的应用程序异常中,我必须能抓住它。大多数时候,一个简单的try / catch做这个工作,因为验证异常是从我已经做过的一个 EntityManager 调用抛出的。



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



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

  @Stateless 
@TransactionManagement(TransactionManagementType.CONTAINER
class TheEJB {

@Inject private EntityManager em;

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public methodOfInterest()throws AppValidationException {
try {
//为了演示的缘故,创建一个导致验证到
的情况//在commit-时间在这里,像
someEntity.getCollectionWithMinSize1()。removeAll();
em.merge(someEntity);
} catch(ValidationException ex){
//不会捕获在集合或其他
// commit时违反@Size - 只有验证异常
抛出新的AppValidationException(ex);
}
}

}

。其中 AppValidationException 是一个检查的异常或未经检查的异常注释的 @ApplicationException ,因此它不会被EJB3包装。



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



帮助?






已尝试:



Bean管理事务可以解决我的问题,让我在我控制的代码内触发提交。不幸的是,他们不是一个选择,因为Bean管理的事务不提供任何等价的 TransactionAttributeType.REQUIRES_NEW - 没有办法使用BMT挂起事务。 JTA的烦人疏忽之一。



请参阅:





...但请查看注意事项和详细信息的答案。



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



我试图使用这样一个 EJB3拦截器

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

...但似乎拦截器发射>内部 JTA(这是明智的,通常是可取的),所以我想抓住的异常还没有被抛出。



我想我想要的是能够定义在 JTA做它的事情之后应用的异常过滤器。任何想法?






我正在使用JBoss AS 7.1.1.Final和EclipseLink 2.4.0。 EclipseLink按照这些说明作为JBoss模块安装,但这对于






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

似乎在任何地方我都可以有效地捕获应用程序异常,我也可以 - 尽管不太漂亮 - 捕获EJBException并在其内部进行JTA异常和基础验证或JDBC异常然后做出决策。没有JTA中的异常过滤器功能,我可能需要。

解决方案

我还没有尝试过。但是我猜测这应该是有效的。

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

@Inject
private TheEJB self;

@Inject私人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 ,因此您应该能够捕获包装器方法中的异常。



Ps:答案是基于@LairdNelson提供的优雅想法更新...


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.

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.

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.

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

}

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

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.

Help?


Already tried:

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.

See:

... but see answers for caveats and details.

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.

I tried to use a EJB3 interceptor like this:

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

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

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


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.


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.

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

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: The answer is updated based on the elegant idea provided by @LairdNelson...

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

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