Is it possible to catch exception inside spring transaction and commit without rollback?
You can specify exceptions to be ignored, in other words the commit will happen.
Example
#Transactional(noRollbackFor = SomeException.class)
Doc is here
Or you can just catch the exception, will have the same effect.
Related
Does Java Connection.close rollback into a finally block?.
I know .Net SqlConnection.close does it.
With this I could make try/finally blocks without catch...
Example:
try {
conn.setAutoCommit(false);
ResultSet rs = executeQuery(conn, ...);
....
executeNonQuery(conn, ...);
....
conn.commit();
} finally {
conn.close();
}
According to the javadoc, you should try to either commit or roll back before calling the close method. The results otherwise are implementation-defined.
In any database system I've worked with, there is no harm in doing a rollback right after the commit, so if you commit in the try block, and rollback in the finally, things get committed, whereas if an exception or early return causes the commit to be missed, the rollback will rollback the transaction. So the safe thing to do is
try {
conn.setAutoCommit(false);
ResultSet rs = executeQuery(conn, ...);
....
executeNonQuery(conn, ...);
....
conn.commit();
} finally {
conn.rollback();
conn.close();
}
Oracle's JDBC driver commits on close() by default. You should not rely on this behaviour if you intend to write multi-platform JDBC code.
The behavior is completely different between different databases. Examples:
Oracle
The transaction is committed when closing the connection with an open transaction (as #Mr. Shiny and New 安宇 stated.
SQL Server
Calling the close method in the middle of a transaction causes the
transaction to be rolled back.
close Method (SQLServerConnection)
For MySQL JDBC, the implementation rolls back the connection if closed without a call to commit or rollback methods.
It is useless to rollback in finally block. After you commit, and commit is successful, why to roll back? So if i were you, i would rollback in catch block.
When a method is annotated with #Transactional and there is an runtime exception, spring eats the exception and throws:
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
How do I avoid this "general" exception and propagate the original exception, but keeping the rollback?
Thanks.
org.springframework.transaction.UnexpectedRollbackException:
Transaction silently rolled back because it has been marked as
rollback-only
It mostly happens if you have an outer #Transactional method calls an inner #Transactional method. When the inner method throws an exception , but the outer method catches this exception and return normally, Spring is confused and don't know if it should rollback or commit the transaction since both methods contradict with each other. (Inner method says it wants to rollback but the outer method says it want to commit)
So , check if there are any outer #Transactional methods that catch the exception. If yes , re-throw that exception from the outer method such that the whole transaction will rollback.
I have a API , That looks like below
#Transaction
void method (){
try{
service1.insertOne();
service2.insertTwo();
}
catch(Exception ex) {
// log exception
}
}
In Service classes, I am checking for certain validation and I am throwing an exception which is a subclass of RuntimeException. When i throw this exception, javax.persistence.RollbackException: Transaction marked as rollbackOnly . While it is preventing the data in the first service from not being inserted , since the second service validation has failed, I am not quite quite sure if this is the right way to handle this scenario.
In case if the Exception is not a sub-class of Exception, even when the validation for service2 fails , data in service1 gets inserted, but i do see the custom exception being thrown. So i am not sure where i am going wrong. Any help is appreciated.
the Spring Framework’s transaction infrastructure code only marks a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException.
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative-rolling-back
So, it must stay as a type of RuntimeException.
Probably you have Transactional annotation on top of service1.insertOne and/or service2.insertTwo() methods and that's why you get javax.persistence.RollbackException: Transaction marked as rollbackOnly.
You don't need those additional annotations (if they're exist) since there is an already active transaction.
Check this answer out.
If your insertOne, and insertTwo methods are marked as #Transactional, default propagation is Required. So you should see all that executed in one transaction. Link to docs
In second case it works as it should as well. If you throw custom transaction, then Spring does not take case of it. You can update that default behavior, by providing rollbackFor. In that case you will expect almost the same situation as in first case.
So it is more question to you - do you want your validation to mark transaction as rollbackOnly, or not. I would say yes.
Perhaps question is too abstract. Let's say you are putting invoice, and positions - then I would expect this transaction to be rolled back. However, if you add group, and users... Group can go, since problem is with user.
Just use the noRollbackForoption of the #Transactional to specify for which exception should not trigger a rollback:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html#noRollbackFor--
#Transaction (noRollbackFor=YourRuntimeException.class)
void method (){
try{
service1.insertOne();
service2.insertTwo();
}
catch(Exception ex) {
// log exception
}
}
I am not quite quite sure if this is the right way to handle this scenario.
YES this way is fine, When you marked your method void method () as Transactional , you instructed whenever there is a runtime exception, please rollback . When a code block with in the #Transactional annotated method observes a runtimeexception during execution, the Spring framework will keep it is as flag ('rollbackOnly') under ThreadLocal variable. Please note that after completion of method() , spring will commit if it finds that the flag ('rollbackOnly') is false. Otherwise spring will instruct the transaction manager to rollback all of the inserts.
even when the validation for service2 fails , data in service1 gets inserted, but i do see the custom exception being thrown. So i am not sure where i am going wrong
As explained above.
With #Transaction and with trace level logging of Spring I see that Hibernate has an exception on a db constraint but it just rolls back the transaction. I tried using #Exception without success to try to catch it.
I see a suggestion online to use Spring AOP #AfterThrowing to catch such events.
This seems like a rather complex way to handle a commonplace event. It seems so much easier with try/catch and old fashioned commits/rollbacks. Is there no better way
in Spring Hibernate?
I'm processing XML messages from a queue. Depending on the type of the exception I get I might want to just catch it and put the bad incoming queue message into an db error table and continue the transaction. (If I just blindly rollback the message will get put back on the queue which might be good in some cases but not in others).
From what I've read there are ways of being made aware of some errors (for some reason not the constraint error) and logging them. Are there ways of interrupting the transaction after an exception and deciding if one wants to commit, continue or rollback?
If not, can one use old style commits and rollback with spring hibernate?
Configure SimpleMappingException resolver and log the exception there:
public class MyExceptionResolver extends SimpleMappingExceptionResolver {
private Logger logger = LoggerFactory.getLogger(MyExceptionResolver .class);
#Override
protected void logException(Exception ex, HttpServletRequest request) {
this.logger.warn(buildLogMessage(ex, request), ex);
}
<bean class="com.mypackage.MyExceptionResolver"/>
EDIT:
You can choose to do anything. The above class has nothing to do with rolling back. It's useful to intercept exceptions and do whatever you want with them.
By default, Spring rolls back for any RuntimeException.
If you want to configure rollback policy:
#Transactional(rollbackFor = { CustomException.class})
or
#Transactional(noRollBackFor= { CustomException.class})
i am working on the Spring "#Transactional" Annotation to handle the transaction.
#Transactional(readOnly = false,propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_UNCOMMITTED,rollbackFor=SQLException.class)
public void updateBatch(Report a)
throws SQLException, DataAccessException { insert1();insert2(); insert3(); }
But in case
insert1() - successfully inserts the data in table A.
insert2() - successfully inserts the data in table b
insert3() - throws the checked exception and does not insert data in table C
these inserts are ibatis inbuild functions to trigger the insert in DB
I got the following exception
"Caused by: org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)"
And the transaction would not get rolledback i.e. insert1(),insert2() does not get rollback
Please do let me know what i am missing
What are you doing in insert1 and insert2?
Are they using another transaction with PROPAGATION.REQUIRES_NEW attribute? If yes, they will not be rolled back. Else are you rolling back in insert1 or insert2?
I've experienced the same problem. Though it might be a bit different as the one you have. What I've had was a situation where you've got two services A and B.
A is calling a method of B in a try-catch block. B throws RuntimeException and the catch block of A wraps that exception into a catched exception.
What happened in my scenario:
- B is a separate class behind a transaction proxy
- B throws a runtime exception which sets transaction to rollback
- as I have Propagation.REQUIERED, it is the same transaction as the one for the method of A
- A wraps the exception and throws it as the exception is neither RuntimeException nor Error, the mention method will not proceed into transactionManager.rollback but to transactionManager.commit
- that would throw the UnexpectedRollback as the transaction was previously marked as rollback.
Your case might be different.