我用管理一个不平凡的数据模型的EJB3类问题所困扰。 我有约束验证异常时,我的容器管理事务方法提交被抛出。 我想,以防止它们被包裹在EJBException
,而不是抛出一个理智的应用程序异常调用者可以处理。
将其包装在合适的应用程序异常,我必须能够抓住它。 大多数时候,一个简单的try / catch做工作,因为验证异常是从抛出EntityManager
调用我做了。
不幸的是,一些限制只在提交时检查。 例如,违反@Size(min=1)
上的映射集合只有当容器管理事务提交一旦离开我在我事务方法的结束控制抓住,。 在验证失败时,我不能赶上抛出的异常,敷,因此容器把它包装在javax.transaction.RollbackException
并包装在一个被诅咒的EJBException
。 调用者捕捉所有EJBException
S和去潜水的事业链试图找出如果它是一个验证问题,这是真的不是很好。
我和容器管理事务的工作,所以我的EJB是这样的:
@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包裹。
有时候,我可以触发早期约束冲突与EntityManager.flush()
和赶上,但并非总是如此。 即使这样,我真的很想能够捕获数据库级别的约束违规在提交时也被延迟约束检查抛出,而那些仅会出现在JTA提交。
救命?
已经尝试:
Bean管理事务将被允许我来触发内的代码,我控制承诺解决我的问题。 不幸的是,他们并不是一个选择,因为Bean管理事务不提供任何相等于TransactionAttributeType.REQUIRES_NEW
-有没有办法暂停使用BMT事务。 一个JTA恼人的疏忽。
看到:
- 我们为什么需要JTA 2.0
- 在J2EE Bean管理的事务暂停 (不这样做!)
......但是请注意事项和细节的答案。
javax.validation.ValidationException
是一个例外JDK; 我不能修改它来添加一个@ApplicationException
注释 ,以防止缠绕。 我不能继承它添加注释; 它是由EclpiseLink,不是我的代码所引发的。 我不知道这标志着它@ApplicationException
将停止阿朱(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从DB和死锁或串行化故障而回滚与SQLSTATE 40P01和40001分别 。 这就是为什么,只是试图确保做法提交不会抛出将无法正常工作。 经过应用程序异常不能被抛出通过JTA提交,因为JTA自然接口不声明它们,但选中@ApplicationException
注释异常应该能成为。
看来,无论我可以有效地捕获到一个应用程序异常我也可以 - 尽管不那么娇滴滴 - 捕获一个EJBException异常,并深入其内部的JTA异常和底层验证或JDBC异常,那么决策中基于这一点。 如果没有JTA异常过滤器的功能,我可能会得。
我没有尝试这样做。 但我猜这应该工作。
@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
,因此,你应该能够赶上在包装方法例外。
PS:答案是基于@LairdNelson提供优雅的理念更新中...
有一点需要注意什么,我说一下REQUIRES_NEW
在原来的问题和BMT。
请参阅EJB 3.1规范,部分13.6.1Bean管理事务划分 ,在集装箱的责任 。 它读取:
容器如下必须管理的客户端调用一个企业Bean实例与bean管理事务划分。 当客户端通过调用的企业bean的客户机视图,业务方法, 容器暂停可与客户端请求相关的任何交易 。 如果与实例关联交易 (如果有状态会话bean实例在以前的一些业务方法启动事务会发生这种情况), 集装箱联营的方法执行这笔交易 。 如果有与bean实例相关联的拦截方法,采取这些行动被调用拦截方法之前。
(斜体矿)。 这一点很重要,因为这意味着一个BMT EJB不会继承具有关联容器管理TX呼叫者的JTA TX。 任何当前TX被暂停 ,因此,如果BMT EJB创建一个TX它是一个新的事务,当它提交它只能提交其事务。
这意味着你可以使用开始,就好像它是有效地提交事务的BMT EJB方法REQUIRES_NEW
,做这样的事情:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
class TheEJB {
@Inject private EntityManager em;
@Resource private UserTransaction tx;
// Note: Any current container managed tx gets suspended at the entry
// point to this method; it's effectively `REQUIRES_NEW`.
//
public methodOfInterest() throws AppValidationException, SomeOtherAppException {
try {
tx.begin();
// For demonstration's sake create a situation that'll cause validation to
// fail at commit-time here, like
someEntity.getCollectionWithMinSize1().removeAll();
em.merge(someEntity);
tx.commit();
} catch (ValidationException ex) {
throw new AppValidationException(ex);
} catch (PersistenceException ex) {
// Go grubbing in the exception for useful nested exceptions
if (isConstraintViolation(ex)) {
throw new AppValidationException(ex);
} else {
throw new SomeOtherAppException(ex);
}
}
}
}
这使我提交控制之下。 在哪里我并不需要一个事务跨越多个不同的EJB几个电话这让我来处理所有的错误,包括在提交时,我的代码中的错误。
在对Bean管理事务的Java EE 6教程页面并没有提到这一点,或任何其他有关BMTS如何调用。
关于BMT不能够模拟通话REQUIRES_NEW
在我链接到博客是有效的,只是目前还不清楚。 如果你有一个bean管理的事务EJB您不能暂停,你才能开始另一个开始的事务。 到一个单独的辅助EJB调用可能会暂停您的TX,给你相当于REQUIRES_NEW
,但我尚未对其进行测试。
这个问题的另一半 - 我要在容器管理的事务,因为我必须在几个不同的EJB和EJB方法完成工作的情况下 - 由防御性编程解决。
实体管理器的早期急于冲洗让我赶上我的JSR330验证规则可以找到任何验证错误,所以我只需要确保它们是完整的,全面的,所以我从来没有从数据库中得到任何的检查约束或完整性违规错误的提交时间。 我不能处理他们干净,所以我需要真正的防守避开他们。
该防御性编码的部分原因是:
- 大量使用的
javax.validation
对实体领域的注解,并使用@AssertTrue
验证方法,其中,这是不够的。 - 在集合上验证约束,这是我很高兴地看到支持。 例如,我有一个实体
A
用的集合B
。 A
必须具有至少一个 B
,因此我加入@Size(min=1)
约束的集合B
它在已定义A
。 - 自JSR330验证器。 我已经添加了几个自定义验证的东西像澳洲商业号码(ABNS),以确保我从来没有尝试发送一个无效的数据库,并触发DB级验证错误。
- 实体管理器的早期潮红与
EntityManager.flush()
这迫使验证发生时,它是你的代码的控制之下,而不是后来当JTA去提交事务。 - 在我的EJB门面特定实体的防守代码和逻辑,以确保不能由JSR330验证被检测不会出现和原因的情况下提交失败。
- 如果可行的话,使用
REQUIRES_NEW
的方法来迫使早提交,并允许我的EJB内处理故障,重试适当等。这有时需要一个帮手EJB来解决与自我调用业务方法的问题。
我仍然无法正常处理,然后重试系列化故障或死锁我就像我使用JDBC直接与Swing的时候,所以这一切的“帮助”从容器已经把我倒退数步在某些领域。 它可以节省巨大的在其他地方繁琐的代码和逻辑量,虽然。
当发生这些错误我已经添加了UI的框架级的异常过滤器。 它看到EJBException
包裹JTA RollbackException
包裹PersistenceException
包裹特定的EclipseLink的异常包裹PSQLException
,检查SQLState和使得基于该决定。 这是荒谬的迂回,但它的工作原理
设置eclipselink.exception-handler
属性指向的一个实现ExceptionHandler
看起来前途无量,但没有成功。
适用于Javadoc ExceptionHandler
是...坏...所以你要看看测试实施并进行测试( 1 , 2 )使用它。 有某种程度上更有用的文档在这里 。
似乎很难用异常过滤器来处理一些特定的情况下,同时保持一切不受影响。 我想陷阱PSQLException
,检查SQLSTATE 23514( CHECK
约束冲突),抛出一个异常有用对于否则不会改变任何东西。 这看起来并不实用。
最后,我已经放弃了这个想法,并走了Bean管理事务如果可能的话(现在我正确地了解他们的工作)和防御方法使用JTA容器管理的事务,以防止不必要的异常。
javax.validation.ValidationException是一个例外JDK; 我不能修改它来添加一个@ApplicationException注解过的,以防止包装
除了你的答案:你可以使用XML描述注释第三方类作为ApplicationException的。
文章来源: How to catch and wrap exceptions thrown by JTA when a container-managed-tx EJB commits?