当前位置: 首页 > 知识库问答 >
问题:

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

单于庆
2023-03-14

我们使用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 上运行)

共有2个答案

公孙弘图
2023-03-14

TransactionalException在提交时抛出,即在代码完全执行后。由于事务被标记为回滚,因此无法进行提交并引发异常。

但是,该事务在执行期间的某个时候被标记为回滚。我假设您没有手动将其标记为回滚,因此必须抛出异常。该异常要么是运行时异常,要么用@ApplicationException(rollback=true)注释。由于您没有获得此异常,因此该异常一定是在某个地方捕获的。您确定没有在代码中捕获从业务方法引发的异常吗?

来回答这个问题...不,我认为重新抛出原来的异常是不可能的,因为它是在不同的时间和地点抛出的。

慕容高卓
2023-03-14

执行此操作的最简单方法似乎是使用 CDI 拦截器来捕获异常。我们可以按如下方式定义 CDI 拦截器:

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

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

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

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

接下来,我们必须将新的拦截器包含在我们的bean.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() 代码时发生的任何错误。通过将 try,catch 从异常更改为侦听器实现类事务接收器中的异常子类,可以更改日志记录粒度。

 类似资料:
  • 问题内容: 我们正在将CDI与CMT(容器管理的事务)一起使用,以连接到Web应用程序中的数据库,并标记从前端调用的需要事务的方法: 这将创建一个新的CDI事务,但是现在,如果在执行此代码块或从此方法调用的任何其他代码块时发生异常,则会抛出错误消息: 无论如何,是否有让CDI重新抛出嵌套错误的方法,以便您可以轻松调试回滚的真正原因是什么? (在Java-EE7,Glassfish 4.0,JSF

  • 我正在与用propagation.requires_new注释的方法的奇怪行为作斗争。 以下是TransactionManager的日志:

  • Spring 4.1.4 Hibernate 4.2.0 JDK 1.8 我的上下文:我有一个控制器调用-->Service-->调用Dao业务功能是删除(在1到许多DB关系中)一些子级,但不是所有子级。然后,在删除了一些子项之后,我尝试删除父项,然后我得到了java.sql.SqlIntegrityConstraintViolationException

  • 情景故事时间: 我“继承”了一个程序,一个用于访问数据库的相当简单的webservice。该程序在某个地方有一个缺陷:它试图更新一个没有更新授权的表。该程序只具有更新数据库队列的权限(Oracle),以保存谁访问了什么信息。这是不受欢迎的行为,现在我纠正了它。注意:这与这个问题本身无关,它只是导致我提出这个问题的原因。 该程序使用Spring+Hibernate来管理和访问数据和事务。 因为程序的

  • 我在tomcat服务器中使用具有多个数据源配置的JTA原子事务。有时我会遇到以下异常: JTA事务意外回滚(可能是由于超时);嵌套的异常是javax。交易回滚异常:事务被设置为仅回滚 出现这种异常的原因是什么?

  • 这很好,但并不总是在代码中抛出运行时异常。因此,我挖掘并发现如下所示的rollbackFor; 现在,我必须更改所有代码,以使用RollBackfor更改@Transactional。但是还有其他方法可以将所有@transaction advice属性更改为rollbackFor=exception.class吗?