java - 您如何找出导致 CDI 事务回滚的异常?

标签 java jakarta-ee transactions cdi

我们将 CDI 与 CMT(容器管理的事务)结合使用以连接到 Web 应用程序中的数据库,并标记从前端调用的需要事务的方法:

@Transactional(value=TxType.REQUIRES_NEW)

这将创建一个新的 CDI 事务,但是现在如果执行此代码块或从此方法调用的任何其他代码块发生异常,它将抛出错误消息:

javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRES_NEW encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.

有没有办法让 CDI 重新抛出嵌套错误,以便您可以轻松调试回滚的真正原因?

(在 Java-EE7、Glassfish 4.0、JSF 2.2.2 上运行)

最佳答案

看起来最简单的方法是使用 CDI 拦截器来捕获异常。我们可以这样定义 CDI 拦截器:

@InterceptorBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionDebugger {
}

一旦我们定义了 CDI 拦截器,我们就需要创建在使用拦截器注解时执行的类。我们定义一个@AroundInvoke 以便我们的代码在我们注释的方法中的代码之前被调用。 invocationContext.proceed() 将调用我们注释的方法并为我们提供它返回的结果(如果有)。所以我们可以在这个调用周围放置一个 try, catch (Exception) 来捕获抛出的任何类型的异常。然后我们可以使用记录器(这里使用 log4j)记录这个异常,并重新抛出异常,以便任何上游代码也被告知它。

重新抛出异常也将允许我们使用 CMT(容器管理事务),因为最终容器将捕获异常并抛出事务 RollbackException。但是,您也可以轻松地将 UserTransactions 与此结合使用,并在捕获到异常时执行手动回滚,而不是重新抛出它。

@Interceptor
@TransactionDebugger
public class TransactionInterceptor {
    private Logger logger = LogManager.getLogger();

    @AroundInvoke
    public Object runInTransaction(InvocationContext invocationContext) throws Exception {
        Object result = null;
        try {
            result = invocationContext.proceed();
        } catch (Exception e) {
            logger.error("Error encountered during Transaction.", e);
            throw e;
        }
        return result;
    }
}

接下来,我们必须在 beans.xml 中包含我们的新拦截器(通常位于 src/META-INF 中),因为在 CDI 中默认情况下不启用拦截器。这必须在使用注释的所有项目中完成,而不仅仅是定义注释的项目。这是因为 CDI 在每个项目的基础上初始化拦截器:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
    <interceptors>
        <class>package.database.TransactionInterceptor</class>
    </interceptors>
</beans>

最后,我们必须注释我们使用新的 CDI 拦截器调用的方法。在这里,我们用@Transactional 注释它们以启动事务,并用@TransactionDebugger 注释它们以捕获事务中发生的任何异常:

@Transactional @TransactionDebugger
public void init() {
    ...
}

这将记录执行 init() 代码时发生的任何错误。可以通过将Interceptor实现类TransactionInterceptor中的try、catch从Exception改为Exception的子类来改变日志记录粒度。

关于java - 您如何找出导致 CDI 事务回滚的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18888572/

相关文章:

.net - MSDTC促进交易分布式的可靠测试吗?

java - 在android中选择文件后找不到文件错误

java - 限制 J2EE servlet 只能从本地主机调用的正确方法是什么?

java - 使用 Eclipse 调试 Servlet(运行 Resin)

hibernate - 如何避免嵌套事务不支持错误?

java - 在 Liberty 中创建 EJB 计时器

hibernate - 如何将spring与hibernate session 和事务管理集成?

MySQL:事务提交后调用触发器

java - 如何使用 OpenJPA 将对象从一个表 "move"转移到另一个表?

java - 静态/类变量的值是如何传递的?