Spring: automatic rollback on checked exceptions
You can't do it for application level with @Transactional , but you can :
variant 1 : extend @Transactional annotation and put it as default value for rollbackfor. But set rollbackFor unchecked exceptions only that you need .With this you can control rollbacks only for case that you sure , and avoid copy past of @Transactional(rollbackFor =MyCheckedException.class)
Like:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=MyCheckedException.class)
public @interface TransactionalWithRollback {
}
And use this annotation instead of standard @Transactional.
variant 2 : you can create extension from AnnotationTransactionAttributeSource and override method determineTransactionAttribute:
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae)
//Determine the transaction attribute for the given method or class.
TransactionAttribute see TransactionAttribute api , there is a method
boolean rollbackOn(Throwable ex) Should we roll back on the given exception?
protected TransactionAttribute determineTransactionAttribute(
AnnotatedElement ae) {
return new DelegatingTransactionAttribute(target) {
@Override
public boolean rollbackOn(Throwable ex) {
return (check is exception type as you need for rollback );
}
};
}
Second approach is not so good as first as you do it really global for transaction manager. Better use custom annotation as you can control it any apply only for methods/classes where you really need it. But if you need it in any case use second variant , it will be your default transnational behavior.
Spring boot @Transactional not rolling back the database inserts
There are several things that break proper transactions in Spring
- Your service method is
private
- You are catching and swallowing exceptions
private
method
The fact that your PublicationServiceImpl
save
method is private
basically makes the @Transactional
on that method useless. As a private
method cannot be proxied, no transactions will apply. Even if it would be public
it wouldn't work as you are calling the method from within the same object, hence the transactionality of that method applies.
To fix, make your method public
and call the save
method from an other class (or make the actual method that is calling save
have the proper @Transactional
.
The fact that is doesn't work is due to the type op AOP being used, by default Spring will use proxies and this is a drawback of using proxy based AOP.
Another solution to make it work with private
methods is to switch to full-blown AspectJ with either compile-time or load-time weaving of the classes. Both require additional setup and that can be tedious.
Catch and swallow exceptions
You have in both your repository as well as your service a try/catch
block. Each of those catches and swallows the exceptions (they are logged but not re-thrown).
For transactions to work properly it needs to see the exceptions. The fact that you are catching and swallowing them, makes the transaction aspect not see them and instead of doing a rollback, do a commit. For the transaction aspect everything is ok because there was no exception.
To fix, remove either the try/catch
or rethrow the exceptions.
why does transaction roll back on RuntimeException but not SQLException
This is defined behaviour. From the docs:
Any
RuntimeException
triggers rollback, and any checked Exception does not.
This is common behaviour across all Spring transaction APIs. By default, if a RuntimeException
is thrown from within the transactional code, the transaction will be rolled back. If a checked exception (i.e. not a RuntimeException
) is thrown, then the transaction will not be rolled back.
The rationale behind this is that RuntimeException
classes are generally taken by Spring to denote unrecoverable error conditions.
This behaviour can be changed from the default, if you wish to do so, but how to do this depends on how you use the Spring API, and how you set up your transaction manager.
Spring transaction: rollback on Exception or Throwable
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 12.5.3 Rolling back a declarative transaction
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.
Or take a look at the DefaultTransactionAttribute
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
Transaction is not getting rolled back though exception is thrown
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 RuntimeException
s. 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)
Spring Transaction - automatic rollback of previous db updates when one db update failes
If you use declarative transaction management, you can lose most of this boilerplate:
TestDaoImpl:
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory f){
this.sessionFactory = f;
}
public void saveOrUpdate(BaseDomainModel baseObject) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(baseObject);
}
And you can control the transaction handling from the service layer using @Transactional
(or xml configuration)
TestServiceImpl:
private TestDao testDao;
public void setTestDao(TestDao d){
this.testDao = d;
}
@Transactional // one transaction for multiple operations
public void someServiceMethod(Collection<BaseDomainModel> data){
for(BaseDomainModel baseObject : data)
testDao.saveOrUpdate(baseObject);
}
Reference:
- Implementing DAOs based on plain
Hibernate 3 API
Spring MVC + Hibernate @Transactional not Rollinback after exception
Your transaction is not getting rollback because there is no exception thrown , in other words saveUpdateDeleteTest
is catching the exception, thats why spring transactional proxy cannot detect any exception and Hence no rollback. Remove the catch block and you will see that transaction will rollback . PLease note that spring transaction rollback follows EJB Conventation i.e.
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.
So in your case you need to customize if you want the transaction to be rolled back on any exception, like this:
@Transactional(rollbackFor = Exception.class)
Related Topics
Changing the Current Working Directory in Java
How to Use Key Bindings Instead of Key Listeners
Array or List in Java. Which Is Faster
How to Run Test Methods in Specific Order in Junit4
Is It Safe to Use a Static Java.Sql.Connection Instance in a Multithreaded System
How to Fix Dagger 2 Error '... Cannot Be Provided [...]'
Why Does Java Have Transient Fields
Why in Java 8 Split Sometimes Removes Empty Strings At Start of Result Array
Simpledateformat Ignoring Month When Parsing
How to Tell Jackson to Ignore a Field During Serialization If Its Value Is Null
Java - Get Pixel Array from Image
Calculating Days Between Two Dates With Java
How to Refer to the Current Type With a Type Variable
How to Initialize an Array in Java
How to Use Different Certificates on Specific Connections
How to Parse Command Line Arguments in Java