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

前端 未结 2 1227
礼貌的吻别
礼貌的吻别 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:

    
    
        
            package.database.TransactionInterceptor
        
    
    

    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.

提交回复
热议问题