When I use transaction in Exception, I use #Transactional(rollbackFor = Exception.class).
But, When I use RuntimeException, I use #Transactional.
What should I choose between rollbackfor in exception and runtimeException?
ex)
public class BadRequestException extends Exception
-> public class BadRequestException extends RuntimeException
Spring provides inbuilt support to rollback to RuntimeException same as EJB. Though, adding rollback support for Application Exception (checked Exception) you can define like this.
#Transactional(rollbackFor = BadRequestException.class)
Spring documentation : "While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this". source
Related
Currently we are facing a spring transaction related issue in our application.
As you can see that in deleteRecord() we are doing a DB operation. But in the next line
a business exception is thrown.
Expected Behavior(As per my knowledge) :
The DB operation should be rolled back as exception is thrown from the next line
Actual Behavior :
Its not getting rolled back. Data is getting inserted to the table
Question :
Why transaction is not getting rolled back ? I dont think its because of the catch block
because deleteRecord() will be executed in new transaction. Please correct me if I am wrong
Code:
class A {
void myMethod() {
for(int i=0 ; i<count ; i++) {
try {
deleteRecord();
} catch(Exception e) {
log.error("Exception caught");
}
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
deleteRecord() throws Exception{
line 1 : deleting record
line 2 : Throwing business exception
}
}
The Spring documentation says the following:
While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.
And
In its default configuration, 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. ( Errors will also -
by default - result in a rollback). Checked exceptions that are thrown
from a transactional method do not result in rollback in the default
configuration
see in 16.5.3: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
This says that the default behavior of the transaction will only rollback for RuntimeExceptions. If you have a own business exception (could be a checked excpetion), you have to explicitly name the exception class the transaction should rollback for:
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = YOUREXCEPTION.class)
change to
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
It is because of #Transaction method call by the method within the same class, does not work if you do not configured Spring to use AspectJ.
Spring #Transaction method call by the method within the same class, does not work?
We are using Spring and Hibernate in our project. We are using Spring transactional annotations and we understand what it does, but we still have some doubts regarding rollbacks.
Let's say we have the following code.
#Transactional
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Autowired
private AnotherService anotherService;
#Override
public void doSomething() throws Exception {
Person person = new Person();
person.setName("John");
personDao.insert(person);
person.setName("Megan");
//What happens if the following call fails and throws and exception.
personDao.update(person);
// What happens if this other service's call fails?
anotherService.doSomethingElse();
}
}
If either the update or the other service's call fails, what would happen to the insert? Would there be a rollback and the insert would never be executed? Or would the insert still persist in the DB? Do I need to declare the following command in each method for the rollback to be executed.
#Transactional(rollbackFor= Exception.class)
If that is the case, what happens if my service call throws the exception? Would it still works? Thanks for the help. I would really liked to understand the way rollbacks are executed inside a service call.
By default if the exception is checked (extends Exception), the rollback won't happen - the transaction will be committed. Otherwise (if defaults were changed or we're talking about unchecked exceptions), the transaction is rolled back and INSERT/UPDATE statements don't persist anything. BTW, this is described in the JavaDocs to the rollbackFor attribute ;)
If the exception is eventually thrown from your Transactional method it doesn't matter where that exception originated.
If you're not sure about the behaviour, you can always debug Spring's HibernateTransactionManager and its superclasses.
I would like to annotate my ServiceActivator with Transactional as below :
#ServiceActivator
#Transactional(rollbackFor = Exception.class)
public Message<MyResult> populate(List<Things> th) {
// inserting in database
// try { throwing an exception } catch...
//doing other stuffs (insersions)
}
I expect to rollback the insersions after throwing an exception.
Unfortunately it dosen't work, I got the insersions in database.
Thanks in advance.
Just to close this topic, I'm providing the answer here. Even if it looks like hijacking it from Gary Russell. Although we are from the same team.
So, the #Transactional as many other aspects in Spring must be enabled. For this purpose there is a special #Enable... annotation - #EnableTransactionManagement. This one scans for the PlatformTransactionManager bean and applies AOP Advices to the methods marked with the #Transactional.
See Reference Manual for more information.
My Spring/Java web application has #Transactional services that can touch the database:
#Transactional
public class AbstractDBService { ... }
Desired functionality is for any uncaught throwable that propagates up beyond the service layer to cause a rollback. Was a bit surprised this isn't the default behaviour but after a bit of googling tried:
#Transactional(rollbackFor = Exception.class)
This seems to work except when an exception is deliberately swallowed and not rethrown. (The particular case is when an entity is not found. Guess this could be redesigned to not throw an Exception but expect there will inevitably be others - e.g. one that springs to mind is an InterruptedException when using Thread.sleep()). Then Spring complains:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is
javax.persistence.RollbackException: Transaction marked as
rollbackOnly
...truncated..
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
Am I missing something here?... Is there a way to tell Spring to rollback on all uncaught throwables?
If you want to rollback on all uncaught Throwables, you can specify that in the annotation:
#Transactional(rollbackFor = Throwable.class)
By default Spring doesn't rollback for Error subclasses, probably because it seems doubtful once an Error is thrown that the JVM will be in a good enough state to do anything about it anyway, at that point the transaction can just time out. (If you try to rollback when an OutOfMemoryError is raised, the most likely outcome is another OutOfMemoryError.) So you may not gain much with this.
When you mention the case of swallowing an exception, there's no way Spring can be expected to know about it because the exception is not finding its way to Spring's proxy (which is implementing the transactional functionality). This is what happens in your RollbackException example, Hibernate has figured out the transaction needs to rollback but Spring didn't get the memo because somebody ate the exception. So Spring isn't rolling the transaction back, it thinks everything is ok and tries to commit, but the commit fails due to Hibernate having marked the transaction rollback-only.
The answer is to not swallow those exceptions but let them be thrown; making them unchecked is supposed to make it easier for you to do the right thing. There should be an exception handler set up to receive exceptions thrown from the controllers, most exceptions thrown at any level of the application can be caught there and logged.
I wonder whether it makes sense to use instead of
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
to use Throwable
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
As I understand catching Error will help us behave correctly even when something really bad happen. Or maybe it wouldn't help?
As I understand catching Error will help us behave correctly even when something really bad happen. Or maybe it wouldn't help?
You don't need to explicitly specify rollbackFor = Throwable.class, because spring will by default rollback the transaction if an Error occurs.
See 1.4.3. Rolling Back a Declarative Transaction
In its default configuration, the Spring Framework’s transaction infrastructure code marks a transaction for rollback only in the case of runtime, unchecked exceptions. That is, when the thrown exception is an instance or subclass of RuntimeException. (Error instances also, by default, result in a rollback). Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.
Or take a look at the DefaultTransactionAttribute
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
Since you are using #Transactional, we can safely assume you are doing your database operations through Spring, Hibernate, or other JDBC wrappers. These JDBC wrappers don't typically throw checked exceptions, they throw runtime exceptions that wrap the JDBC SQLException types.
#Transactional is setup to rollback, by default, only when an unchecked exception is thrown.
Consider a use case like so
#Transactional
public void persistAndWrite(Bean someBean) throws IOException {
// DB operation
getSession().save(someBean);
// File IO operation which throws IOException
someFileService.writeToFile(someBean);
}
You wouldn't necessarily want to rollback the DB operation just because we couldn't write something to a file.
Similarly
#Transactional
public void persistAndThrowOutOfMemory(Bean someBean) {
// DB operation
getSession().save(someBean);
// consumes all memory, throws OutOfMemoryError
someService.hugeOperationThrowsOutOfMemoryError();
}
You wouldn't necessarily want to rollback the saved entity just because some service cause too much memory to be consumed.
#Transactional gives you the option. Use it where appropriate.
Default value of rollback is register on Error Exception but when u register try{}catch{} manually it overriding the error, so in this case use
catch {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
to do it manually or remove try catch
also you may register exception type in transactional annotation such:
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
I don't know is it possible or not but handling Throwables like Errors is a bad style of programming, it is not the developers responsibility to handle this kind of fatal errors. There always can happen bad things which You cannot handle. You should handle checked exceptions if necessary, which are known to your system like some type of logical errors.