您如何找出导致CDI事务回滚的异常? [英] How do you find out what Exception caused the CDI Transaction Rollback?

查看:126
本文介绍了您如何找出导致CDI事务回滚的异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  @Transactional(value = TxType.REQUIRES_NEW)

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

  javax.transaction.TransactionalException:带有事务注释和TxType为REQUIRES_NEW的托管bean在提交javax.transaction.RollbackException时遇到异常:事务标记为回滚。 
...
原因:javax.transaction.TransactionalException:具有事务注释和TxType为REQUIRES_NEW的托管bean在提交javax.transaction.RollbackException时遇到异常:事务标记为回滚。
...
原因:javax.transaction.RollbackException:事务标记为回滚。

是否仍然可以通过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
公共类TransactionInterceptor {
私有Logger logger = LogManager.getLogger();

@AroundInvoke
公共对象runInTransaction(InvocationContext invocationContext)引发异常{
Object result = null;
try {
结果= invocationContext.proceed();
} catch(Exception e){
logger.error(交易过程中遇到错误。,e);
投掷e;
}
返回结果;
}
}

接下来,我们必须在我们的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注释捕获事务中发生的任何异常:

  @事务性@TransactionDebugger 
public void init(){
...
}

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


We are using CDI with CMT (container managed transactions) to connect to the database in the web app and mark methods called from the front-end that require a transaction with:

@Transactional(value=TxType.REQUIRES_NEW)

This will create a new CDI transaction, however now if an exception occurs doing this code block or any other code block called from this method it will throw the error message:

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.

Is there anyway to get CDI to re-throw the nested error so that you can easily debug what the real cause of the rollback was?

(Running on Java-EE7, Glassfish 4.0, JSF 2.2.2)

解决方案

It seems the easiest way to do this is by using a CDI Interceptor to catch the exceptions. We can define the CDI Interceptor as follows:

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

Once we have defined the CDI Interceptor, we need to create class that is executed when the Interceptor annotation is used. We define an @AroundInvoke so that our code is called before the code in the method we annotate. invocationContext.proceed() will call the method we annotate and give us the result (if any) it returned. So we can put a try, catch (Exception) around this call to catch any kind of Exception that is thrown. Then we can log this exception using the logger (log4j used here), and re-throw the Exception so that any upstream code is also informed of it.

Re-throwing the Exception will also allow us to use CMT (Container Managed Transactions) as then ultimately the container will catch the Exception and throw a transaction RollbackException. However, you could easily use UserTransactions with this also and perform a manual rollback when an exception is caught instead of re-throwing it.

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

Next we must include our new Interceptor in our beans.xml (generally located in src/META-INF), since Interceptors are not enabled by default in CDI. This must be done in ALL projects where the annotation is used, not just the project where the annotation is defined. This is because CDI initializes Interceptors on a per project basis:

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

Finally, we must annotate the methods that we call with our new CDI Interceptor. Here we annotate them with @Transactional to start a transaction, and @TransactionDebugger to catch any Exception that occurs within the transaction:

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

This will now log ANY error that occurs while executing the init() code. The logging granularity can be changed by changing the try, catch from Exception to a sub-class of Exception in the Interceptor implementation class TransactionInterceptor.

这篇关于您如何找出导致CDI事务回滚的异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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