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.
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.
Related
I have the bellow case, Where I call doSomeTask() of BeanA but if doSomeTask() fails I want to persist ErrorInfo into another table as well calling the saveError(ErrorInfo) of BeanA. Both of them has #TransactionAttribute(REQUIRES_NEW).
class BeanA {
#TransactionAttribute(REQUIRES_NEW)
public void doSomeTask(){
if(someCondition){
throw new SomeException();
}
// do task
}
#TransactionAttribute(REQUIRES_NEW)
public void saveError(ErrorInfo error) {
// save error info if doSomeTask fails
}
}
class BeanB {
BeanA beanA;
void performTask(){
try{
beanA.doSomeTask();
}catch(Exception e){
ErrorInfo error = getErrorInfo(e)
beanA.saveError(error);
}
}
}
But when doSomeTask() throws Exception saveError() doesn't work and throws Exception
Caused by: weblogic.transaction.internal.AppSetRollbackOnlyException: setRollbackOnly called on transaction
What am I doing wrong and how to fix this error? Thanks in advance for any help.
Sorry, for the late answer. The problem was resolved.
The actual error was hidden. In my case, the actual error was just a JSR 303 validation error of ErrorInfo instance while persisting. Had to add
Dweblogic.transaction.allowOverrideSetRollbackReason=true
on <domain_home>/bin/setDomainEnv.sh to find out the actual error and fix it. Thanks to this answer https://stackoverflow.com/a/38584687/1563286
I have debugged the similar issue not so long ago. In my case the issue was the following:
there was a top level transaction open when the REQUIRES_NEW method is called
After exception in nested transaction commit of the top level one failed to commit as "marked as rollback only"
It turned out that through a new transaction is started the connection holder is shared on TransactionManager level. When exception is thrown inside nested transaction the connection itself is marked as rollback only. So later this is causing the issue.
I was able to resolve the issue by using the savepoints (available since JDBC 3.0). Usually savepoints are disabled in many environments/ORM by default and using them requires additional configuration.
Hope this is of some help.
My setup:
I have an EJB bean (deployed in an ear on a JBoss 6.4) with a method that calculates some values and stores it in a database. The method is started in a new transaction. It looks like this:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doStuff() {
MyObject value = calculateSomeValues();
entityManager.merge(value);
entitiyManager.flush();
}
By design, it is possible that the flush will fail due to a ConstraintViolationException. As this is a new transaction, the exception will be wrapped in an EJBTransactionRolledbackException and thrown as a RuntimeException. I have set up logging for ejb exceptions as it is useful to find them in the whole system. However as I know that the ConstraintViolationException will occur now and then, I would like to catch it and rollback before the logging system logs it as an error (the constraint violation is not seen as an exception, but it is required from the database's point of view). At the moment, my log file is clogged with entries like this one:
ERROR [org.jboss.as.ejb3.invocation] (default-threads - 49) JBAS014134: EJB Invocation failed on component
How can I catch the exception in such a way that the logger is prevented from printing these errors? (Note: I still want it to log all other EJBTransactionRolledbackExceptions)
I don't have JBoss 6 deployed, so I cannot immediately test whether the precise exception types work also for JBoss 6 (I tried this on WildFly 8).
A possible solution is to put in your EJB a method annotated by #AroundInvoke (at least if you don't have other interceptors on this EJB):
#AroundInvoke
public Object myConstraintViolationInterceptor(InvocationContext ctx) throws Exception
{
try {
return ctx.proceed();
}
catch(PersistenceException e) {
if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
//some action taken
}
else {
throw e;
}
return null;
}
}
By the way, if your ConstraintViolationException is actually optimistic locking exception in disguise (that is, it is thrown because another user has modified some data in the meantime), maybe it is better to report it to the user.
I am trying to solve this error in Payara41 server Java EE 7, this sample works on WildFly-9 Java 7 EE and on Glassfish-3.1 Java EE 6 (without #Transactional and #TransactionalManagement)
#Stateful
#Transactional //default TxType.REQUIRED
#TransactionManagement(TransactionManagementType.BEAN)
public class ImprovementDaoImpl extends AbstractBaseDaoClass implements ImprovementDao {
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#PersistenceContext(unitName = "pu", type = PersistenceContextType.EXTENDED)
private EntityManager em;
#Resource
private UserTransaction tx;
...
}
Here's some stacktrace, what apperas after executing tx.flush();:
javax.transaction.TransactionalException: Managed bean with Transactional annotation and TxType of REQUIRED encountered exception during commit javax.transaction.RollbackException: Transaction marked for rollback.
(...)
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.
So far I've tried to use interceptor and #TransactionAttribute, but none helped...
Thanks for any advice/help! :)
I know this is old, but hopefully this helps someone out there...
Question 21363423: Throwing an application exception causes TransactionalException says
You are throwing an exception from a method whose invocation will be
intercepted at runtime and additional logic wrapped around it:
transaction management;
exception handling.
Your exception cannot transparently jump over that logic, and the
specification (probably) says a TransactionalException will be thrown,
wrapping your original exception...
Question 18888572: How do you find out what Exception caused the CDI Transaction Rollback?
Shows how to use a CDI Interceptor to catch the exceptions. I can't tell from the limited info from the OP what his/her specific issue is, but when I received this exception I has to review the WebLogic server log and found the entry where it told me a unique contraint was violated.. Time to add some interceptors...
I am having issues with committing a transaction within my #Transactional method:
methodA() {
methodB()
}
#Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
When I call methodB() from methodA(), the method passes successfuly and I can see "OK" in my logs. But then I get
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at methodA()...
The context of methodB is completely missing in the exception - which is okay I suppose?
Something within the methodB() marked the transaction as rollback only? How can I find it out? Is there for instance a way to check something like getCurrentTransaction().isRollbackOnly()? - like this I could step through the method and find the cause.
When you mark your method as #Transactional, occurrence of any exception inside your method will mark the surrounding TX as roll-back only (even if you catch them). You can use other attributes of #Transactional annotation to prevent it of rolling back like:
#Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
I finally understood the problem:
methodA() {
methodB()
}
#Transactional(noRollbackFor = Exception.class)
methodB() {
...
try {
methodC()
} catch (...) {...}
log("OK");
}
#Transactional
methodC() {
throw new ...();
}
What happens is that even though the methodB has the right annotation, the methodC does not. When the exception is thrown, the second #Transactional marks the first transaction as Rollback only anyway.
To quickly fetch the causing exception without the need to re-code or rebuild, set a breakpoint on
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3
and go up in the stack, usually to some Interceptor. There you can read the causing exception from some catch block.
I struggled with this exception while running my application.
Finally the problem was on the sql query. i mean that the query is wrong.
please verify your query. This is my suggestion
Found a good explanation with solutions: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) remove the #Transacional from the nested method if it does not really require transaction control. So even it has exception, it just bubbles up and does not affect transactional stuff.
OR:
2) if nested method does need transaction control, make it as REQUIRE_NEW for the propagation policy that way even if throws exception and marked as rollback only, the caller will not be affected.
Look for exceptions being thrown and caught in the ... sections of your code. Runtime and rollbacking application exceptions cause rollback when thrown out of a business method even if caught on some other place.
You can use context to find out whether the transaction is marked for rollback.
#Resource
private SessionContext context;
context.getRollbackOnly();
There is always a reason why the nested method roll back. If you don't see the reason, you need to change your logger level to debug, where you will see the more details where transaction failed. I changed my logback.xml by adding
<logger name="org.springframework.transaction" level="debug"/>
<logger name="org.springframework.orm.jpa" level="debug"/>
then I got this line in the log:
Participating transaction failed - marking existing transaction as rollback-only
So I just stepped through my code to see where this line is generated and found that there is a catch block which did not throw anything.
private Student add(Student s) {
try {
Student retval = studentRepository.save(s);
return retval;
} catch (Exception e) {
}
return null;
}
disable the transactionmanager in your Bean.xml
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
comment out these lines, and you'll see the exception causing the rollback ;)
apply the below code in productRepository
#Query("update Product set prodName=:name where prodId=:id ")
#Transactional
#Modifying
int updateMyData(#Param("name")String name, #Param("id") Integer id);
while in junit test apply below code
#Test
public void updateData()
{
int i=productRepository.updateMyData("Iphone",102);
System.out.println("successfully updated ... ");
assertTrue(i!=0);
}
it is working fine for my code
Below is the exception I am getting while initializing any ejb object.
Could any one tell what is the reason of below exception and how can I resolve it???
Is is Application exception or Environment Exception???
Thanks in Advance... waiting for response ...
[7/12/10 5:05:24:226 EDT] 00000037 ExceptionUtil E CNTR0020E: EJB threw an unexpected (non-declared) exception during invoc
ation of method "init" on bean "BeanId(RDxEAR#vs-tle-beans-server.jar#VLSContextHome, C5E6CBE5-0129-4000-E000-C9DF093361B8)".
Exception data: java.rmi.RemoteException
at com.versata.tl.vls.ejb.VLSContextBean.init(VLSContextBean.java:298)
at com.versata.tl.vls.ejb.EJSRemoteStatefulVLSContextHome_acff79a1.init(Unknown Source)
at com.versata.tl.vls.ejb._EJSRemoteStatefulVLSContextHome_acff79a1_Tie.init(_EJSRemoteStatefulVLSContextHome_acff79a
1_Tie.java:2119)
at com.versata.tl.vls.ejb._EJSRemoteStatefulVLSContextHome_acff79a1_Tie._invoke(_EJSRemoteStatefulVLSContextHome_acff
79a1_Tie.java:395)
at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:621)
at com.ibm.CORBA.iiop.ServerDelegate.dispatch(ServerDelegate.java:474)
at com.ibm.rmi.iiop.ORB.process(ORB.java:503)
at com.ibm.CORBA.iiop.ORB.process(ORB.java:1571)
at com.ibm.rmi.iiop.Connection.respondTo(Connection.java:2703)
at com.ibm.rmi.iiop.Connection.doWork(Connection.java:2577)
at com.ibm.rmi.iiop.WorkUnitImpl.doWork(WorkUnitImpl.java:62)
at com.ibm.ejs.oa.pool.PooledThread.run(ThreadPool.java:118)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1473)
[7/12/10 5:05:24:231 EDT] 00000037 LocalTranCoor E WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
Looks like the 'Caused by' information is not there, but it seems by part of the stack trace that the Stateful bean's #Init method corresponding to the EJBHome.create() method is throwing a RuntimeException. The 'Resources rolled back' message is likely not the cause of the problem but simply the container carrying it it's job of cleaning up after the runtime exception.
First thing to do is either:
1. Surround the body of the init() method with something that catches and logs the RuntimeException so you can look at where the real source of the problem is, or better...
2. Put that exception logging into an interceptor so it can easily be reused on any other beans in the future.
If you do find the RuntimeException and determine it is an exception you do what to throw without a transaction rollback or your bean instance being destroyed you can use the #ApplicationException annotation on the exception class to turn it into an exception that is safe to throw. If it is a built in exception you can mark it in the ejb-jar.xml file. Note, though, that this affects all usage of the exception type, so be very careful and avoid doing things like java.lang.RuntimeException. Best to mark very specific subclasses of RuntimeException to avoid completely disabling the container's ability to clean up after unexpected exceptions.