I am getting the below exception when doing a batch processing
encountered an error.org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
Could someone help me with what might be the issue?
Thanks.
Probably you have a checked exception somewhere in one of your beans that you catch in another one, but the transaction advice of spring notices it and will mark the transaction for rollback.
You can play with the noRollBackFor property of the Transactional annotation.
The possibility of getting this exception in Spring-managed environments is when the transaction propagation is set to REQUIRED:
<tx:method name="do*" propagation="REQUIRED" />
Consider a scenario as this:
Caller -------> [Transactional Method1(m1)] ----------> [Transactional
Method2(m2)]
In Spring-managed environments, there is a difference between logical and physical transactions. A logical transaction scope is created for each method upon which this setting is applied. The logical transaction scope for method m1 is different from m2. Each logical transaction can determine its own rollback-only status individually. And with this setting, an outer transaction scope (m1's scope) is logically independent from the inner transaction scope (m2's scope).
But all these scopes are mapped to the same physical transaction. So, if the inner transaction is marked for rollback, it effects the outer transaction's chance to commit (even if there is no exception thrown from the the outer transaction).
Now if there is an exception thrown from inner transaction and it is marked for rollback. But there is no exception thrown from outer transaction, so it has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. And a corresponding UnexpectedRollbackException is thrown at that point.
So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller receives an UnexpectedRollbackException if it still calls commit. This is an indication to the outer caller that a rollback was performed instead of a commit. And this an expected behavior to let the caller of a transaction know that there was an exception and the the transaction was rolled back.
Related
Recently refactoring some code, came across transaction rollback scenario where one EJB bean calls another. Now in exception block each bean has its context which is marked for rollback.
Is this a good practice or they should just re-throw the exception & finally the initiator bean only does the rollback.
Also, if there is single transaction spanned across EJB's, then rollback should happen at the originator bean or where it encountered exception.
Transaction type JTA for persistence with XA data source.
With this
is marked for rollback
you mean that the EJB catch the exception and use setRollbackOnly ?
If that's the case, then it depends on your design to decide which approach is preferred.
Normally a transaction is rolled back if a system exception is detected by the container. Application exception on the opposite does not have this effect.
But, if your business logic require that even a business exception has an important impact such that it must have the effect of rolling back the whole transaction, then you have the choice of setRollBackOnly, or launch an application exception with rollback=true.
This second approach has also the effect of not destroying the bean.
Regarding your second question:
Also, if there is single transaction spanned across EJB's, then
rollback should happen at the originator bean or where it encountered
exception.
the rollback is managed by the container, and again it depends on your design. Keep in mind that the error may pass trough the hole of your unique bean in charge of rolling back the transaction, and not being catched at all. So you will end up with an unwanted scenario with a transaction not being rolled back at all.
I'm reading the Hibernate documentation at present and I came across the following quote:
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
This all makes sense as far as I'm concerned, but it does make me wonder what the effect of closing a session is with a transaction neither committed nor rolled back?
E.g consider the following:
session = getSessionFactory().openSession();
session.beginTransaction();
session.save(carObject);
//session.getTransaction().commit();
session.close();
With commit commented out, and no rollback called here, what is the expected behaviour of session.close()? Does it simply rollback that commit automatically, does it leave a 'hanging' transaction? etc.
(I understand this obviously wouldn't be good practice - I'm just trying to get my head around the underlying concepts a bit more.)
I've done a bit of digging into Hibernate:
Persistence sessions keep their life-cycle somewhat independent from JDBC connections. When you close Hibernate's Session the connection is released. Exact meaning of "releasing connection" depends on how the connection was obtained in the first place:
if the connection was provided manually (e.g. via sessionFactory.openStatelessSession(connection)) you will get your connection with possibly unfinished transaction back when calling session.close()
in other cases calling session.close() will usually end up in calling connection.close()
No automatic session flushing or transaction commit / rollback is made by Hibernate. The same states for the JPA's EntityManager.
So what happens in the end depends on your connection provider / data source. With C3PO any unfinished transaction will be rolled-back when the connection is returned to the pool. On the other hand if you have managed JTA connection then the actual transaction handling might be completely out of scope to your application.
I have a DBManager singleton that ensures instantiation of a single EntityManagerFactory. I'm debating on the use of single or multiple EntityManager though, because a only single transaction is associated with an EntityManager.
I need to use multiple transactions. JPA doesn't support nested transactions.
So my question is: In most of your normal applications that use transactions in a single db environment, do you use a single EntityManager at all? So far I have been using multiple EntityManagers but would like to see if creating a single one could do the trick and also speed up a bit.
So I found the below helpful: Hope it helps someone else too.
http://en.wikibooks.org/wiki/Java_Persistence/Transactions#Nested_Transactions
Technically in JPA the EntityManager is in a transaction from the
point it is created. So begin is somewhat redundant. Until begin is
called, certain operations such as persist, merge, remove cannot be
called. Queries can still be performed, and objects that were queried
can be changed, although this is somewhat unspecified what will happen
to these changes in the JPA spec, normally they will be committed,
however it is best to call begin before making any changes to your
objects. Normally it is best to create a new EntityManager for each
transaction to avoid have stale objects remaining in the persistence
context, and to allow previously managed objects to garbage collect.
After a successful commit the EntityManager can continue to be used,
and all of the managed objects remain managed. However it is normally
best to close or clear the EntityManager to allow garbage collection
and avoid stale data. If the commit fails, then the managed objects
are considered detached, and the EntityManager is cleared. This means
that commit failures cannot be caught and retried, if a failure
occurs, the entire transaction must be performed again. The previously
managed object may also be left in an inconsistent state, meaning some
of the objects locking version may have been incremented. Commit will
also fail if the transaction has been marked for rollback. This can
occur either explicitly by calling setRollbackOnly or is required to
be set if any query or find operation fails. This can be an issue, as
some queries may fail, but may not be desired to cause the entire
transaction to be rolled back.
The rollback operation will rollback the database transaction only.
The managed objects in the persistence context will become detached
and the EntityManager is cleared. This means any object previously
read, should no longer be used, and is no longer part of the
persistence context. The changes made to the objects will be left as
is, the object changes will not be reverted.
EntityManagers by definition are not thread safe. So unless your application is single threaded, using a single EM is probably not the way to go.
I have a question about Spring transaction propagation.
Suppose I use #Transactional(propagation = Propagation.REQUIRED) to annotate a method m1(). When execution logic enters m1(), if there is already a transaction, m1() will use that one. When after m1(), what about the transaction? It will end or still remain open? (if I call m1() in another method, and after the invocation there is still other things to do).
In summary, I want to know when exiting an annotated method, the transaction ends or still remains open?
Great thanks.
Propagation.REQUIRED (documented here) will create a new transaction (if none exists for the current thread), or will join an existing transaction (if one exists).
When the method exits, then the transaction will be completed (if entering the method caused a transaction to be created), or will leave the transaction open (if a transaction already existed when the method was entered). In other, words, it's symmetrical, and will leave the thread's transactional state in the same state it was before the method was entered.
Is there any way to "replay" transaction?
I mean, sometimes I get RollbackException and rollback the transaction. Can I then "clone" the transaction and try again, or once rollback is called, transaction is lost?
I really need the changes, and really don't want to trace every change for rerunning later...
thanks,
udi
Why do you get the exception in the first place ? This seems to me to be the crux of the matter.
Are you relying on optimistic writing ? If so, then you'll have to wrap your database writes in some form of loop, incorporating (perhaps) a backoff and a number of retries. You can't do this automatically, unfortunately (unless you investigate some form of AOP solution wrapping your database writes with a retry strategy ?)
That depends where that transaction comes from. In Java/JDBC, a transaction is tied to a connection. You start one by setting setAutoCommit() to false (otherwise, every statement becomes its own little transaction).
There is nothing preventing you from reusing the connection after a transaction failed (i.e. you called rollback).
Things get more tricky when you use Spring. Spring wraps your methods in a transaction handler and this handler tries to guess what it should do with the current transaction from the exceptions that get thrown in the method. The next question is: Which wrapper created the current transaction? I just had a case where I would call a method foo() which would in turn call bar(), both #Transactional.
I wanted to catch errors from bar() in foo() and save them into the DB. That didn't work because the transaction was created for foo() (so I was still in a transaction which Spring thought broken by the exception in bar()) and it wouldn't let me save the error.
The solution was to create baz(), make it #Transactional(propagation=Propagation.REQUIRES_NEW) and call it from foo(). baz() would get a new, fresh transaction and would be able to write to the DB even though it was called from foo() which already had a (broken) transaction.
Another alternative is to use JDBC savepoints to partly roll back.