How do you find out what Exception caused the CDI Transaction Rollback?

前端 未结 2 1228
礼貌的吻别
礼貌的吻别 2021-02-10 03:13

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

相关标签:
2条回答
  • 2021-02-10 03:47

    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.

    0 讨论(0)
  • 2021-02-10 04:09

    The TransactionalException is thrown at the commit time, i.e. after your code has been fully executed. Since the transaction is marked for rollback, commit cannot take place and the exception is thrown.

    However, the transaction was marked for rollback sometime during the execution. I assume you do not mark it for rollback manually, therefore an exception must have been thrown. The exception is either a RuntimeException or it is annotated with @ApplicationException(rollback = true). Since you don't get this exception, the exception must have been caught somewhere. Are you sure you do not catch exceptions thrown from a business method in your code?

    To answer the question... No, I don't think it is possible to rethrow the original exception, because it is thrown at different time and place.

    0 讨论(0)
提交回复
热议问题