【问题标题】:How to check in EJB interceptor if the intercepted method got a RollbackException after context.proceed()?如果被拦截的方法在 context.proceed() 之后出现 RollbackException,如何检查 EJB 拦截器?
【发布时间】:2015-01-07 11:52:37
【问题描述】:

我目前正在开发一个企业解决方案,该解决方案被部署为 weblogic12c 中的 EAR 模块。我需要编写一个业务逻辑,其中将有实体管理器方法,这些方法使用带有EclipseLinkEntityManager 接口对数据库执行一些更新,并且我需要在这些方法周围有一个拦截器,它将拦截方法调用如果目标方法没有失败,则将一些数据推送到 JMS 队列。

由于拦截器与被拦截方法存在于相同的事务和安全上下文中,因此如果一个失败,它会导致另一个失败,即如果通过抛出 EJBException 来完成。我的问题是关于那些在事务实际提交之前无法捕获的事务回滚异常。所以即使被拦截的方法得到RollbackException,拦截器也不会立即终止,因为它运行在同一个事务模型中,所以它无法捕获那些回滚异常。最终它也被回滚,但与此同时,数据被推送到 JMS 队列中,这并不是真正需要的。

我也不能将 JMS 队列部分从拦截器中取出,因为作为软件要求,如果模块无法将数据推送到 jms,事务也应该失败并回滚。所以我在这里想要实现的是,如果被拦截的方法失败,事务也会失败,并且拦截器中不应该有任何超出 context.proceed() 方法调用的执行。

现在,假设我有一个业务方法:

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Interceptors(TestInterceptor.class)
@Override
public TestBean update(TestBean testBean) {
    try {
        TestEntity testEntity = em.createNamedQuery("TestEntity.findById", TestEntity.class)
                .setParameter("id", testBean.getId())
                .getSingleResult();
        // only some bean conversions before merging
        testEntity.setName(testBean.getName());
        testEntity.setPhone(testBean.getPhone());
        testEntity.setImage(testBean.getImage());
        em.merge(testEntity);
        em.flush();
        testBean.setVersion(testEntity.getVersion());
        logger.debug("Update successful");
        return testBean;
    } catch (Exception ex) {
        logger.debug("update::exception: ", ex);
        throw ex;
    }
}

还有一个网络服务方法:

@MethodPrivilege(MethodRole.GENERIC)
@WebMethod
@Override
public String testDataCRUD(@WebParam(name = "auth") AuthRequest authRequest,
                           @WebParam(name = "id") Long id,
                           @WebParam(name = "name") String name,
                           @WebParam(name = "phone") String phone,
                           @WebParam(name = "image") byte[] image,
                           @WebParam(name = "action") Action action) {
    ...
    // build testBean from received parameters
    ...
    try {
        testManager.update(testBean);
        return "SUCCESS";
    } catch(Exception ex) {
        return "FAILED -- UPDATE Error: " + ex.getMessage();
    }
}

最后是拦截器方法:

@AroundInvoke
private Object around(InvocationContext ctx) throws Exception {
    Object testParam = null;
    TestBean testBean = null;

    logger.debug("Test intercepted");

    try {
        testParam = ctx.proceed();
    } catch(Exception ex) {
        logger.debug("Interceptor exception ", ex);
        throw new EJBException("Target method raised an exception");
    }
    // do some bean conversion here
    ...
    ...
    // construct the message object here
    boolean success = jmsNotifier.send(message, false);
    if(!success) {
        throw new Exception("JMS failed.");
    }

    logger.debug("Successfully sent to jms");

    return testParam;
}

现在,如果业务方法收到 RollbackException,服务层可以捕获它,因为业务方法启动了一个新事务。但到目前为止,我无法在 ctx.proceed(); 周围的 try catch 块中捕捉到它。结果,代码继续并将数据推送到 jms 队列,然后在事务完成时获取异常。我该如何解决这个问题? 注意:抱歉我的英语不好,如果我需要进一步澄清其中的某些部分,请告诉我。

更新:(Display Name is missing的评论后)

为了描述我这样做的原因 - 例如,我正在向现有的 ejb 项目添加一个额外的 jar 包,服务层和业务/实体管理器层来自现有的应用程序。现在,我添加的附加 ejb jar 围绕目标业务方法执行了一些额外的工作,并且可以有很多这样的方法。所以我的任何同事都可以抓住这个罐子,将其添加到他们的项目中,我需要一种方法,以便他们需要对他们的业务或实体方法进行最少的更改。到目前为止,我所尝试的是,他们需要做的就是添加一个拦截器类,然后它可以使用我的插件库的功能。那样的话,业务层唯一需要做的改变就是多加一行说明@Interceptor注解和现有的代码库没有被很大程度的修改。

还尝试添加 em.flush() 但它在所描述的情况下没有帮助。非常感谢任何帮助。

【问题讨论】:

  • 我相信这和这个问题一样,它也谈到了包装困难ctx.proceed():stackoverflow.com/questions/11774722/…
  • 您所指的帖子中的建议答案需要对业务层代码进行大量修改。我的情况有点不同。我正在编写一个通过 Interceptor 注释添加的可插拔模块。业务逻辑来自现有的应用程序代码,我不能(或者更准确地说,不想)在很大程度上修改。

标签: jakarta-ee transactions ejb interceptor weblogic12c


【解决方案1】:

两件事-

1) RollbackException 是运行时异常,所以 catch(Exception e) 不起作用。你需要像 catch(Throwable t) 这样的东西来拦截所有东西。

2) 您可以将 JMS 逻辑放在 try 块中,这样如果 ctx.proceed() 抛出,您将永远不会遇到 JMS 代码。

希望这会有所帮助。

【讨论】:

  • 好久不见,多谢回复。我还没有尝试捕捉可投掷的物体。事实上,我什至不知道它可以捕获回滚异常。你确定吗?关于#2,我正在抛出异常,所以我认为不会执行 jms 代码。
  • 除非拦截器堆栈在它到达你之前吃掉它,否则你应该能够做到,但由于你在日志中看到它,我认为这不太可能。
  • 抱歉这么晚回复,但我的尝试失败了。只有在 jpa 尝试提交后才会生成回滚异常。此外,似乎很难捕捉到特定的 jpa 异常......
猜你喜欢
  • 2014-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多