Reputation: 11
I have a set of operation i would like to be rollbacked if there are an error.
My class
public class BSException extends RuntimeException{
...
}
public class saleFacade{
public update(){
for (){
try{
renewSale();
}
catch(BSException){
logger.error();
}
}
}
@Transactional
public renewSale(){
try{
findSale(); // read only Transactional
xxx.renewSpecialSale();
}
catch(Exception e){
logger.error(...);
}
}
}
public class xxx(){
public void renewSpecialSale(){
payFee(); //write to db
if(error){
throw new BSException();
}
}
@Transactional(propagation = Propagation.REQUIRED)
public payFee(){
try{
...
}
catch(BsException e){
...
}
catch(Exception e){
...
}
}
}
@Configuration
@EnableTransactionManagement
public class DBConfiguration{
@Bean(name = "dataSource")
public BasicDataSource dataSource(){
...
}
}
Inn renewSpecialSale error is throw.
In the renewSale method, if there is an error, i would like to rollback. Right now nothing is rollbacked
any idea?
Upvotes: 1
Views: 229
Reputation: 96454
If you catch the exception before it leaves the method, then there is no way the proxy wrapping the method can know that an exception was thrown.
Either remove the try-catch entirely or rethrow the exception so that the exception can leave the method that you marked @Transactional (and get intercepted by the proxy), and the rollback will take place.
I recommend removing exception-handling from all these methods. Set up a central exception handler so that anything thrown from the controllers gets caught and logged, and otherwise let exceptions get thrown.
Make sure each of these classes that does something transactional is annotated separately, if you annotate on the method-level then each method that does something transactional should be annotated. Calling a transactional method from a non-transactional method on the same object doesn't go through the proxy (the proxy intercepts only those calls coming in from outside the object, and only then if that method is marked as transactional) so it isn't part of a transaction (+1 to Peter's answer for pointing this out).
I have no idea what your error flag is doing, it seems odd. Spring services shouldn't have state. If you fix the exception-handling you shouldn't need error flags.
Upvotes: 2
Reputation: 793
The problem comes from your nesting of Method calls and the usage of @Transactional. By default Springs Transaction Management
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Spring Transaction Management
This means, that the call of
xxx.payFee()
is not surrounded by a transaction when it's called through
saleFacade.update() -> saleFacade.renewSale() -> xxx.renewSpecialSale()
As far as I got it, you have at least these options
Upvotes: 1
Reputation: 3005
remove try catch block from your method and put below line of code
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
use this line of code insted of
@Transactional(propagation = Propagation.REQUIRED)
In above code spring container handle whole transaction management and here if we provide rollback attribute it automatically manage if any kind of exception occur it will rollback your transaction
and make sure that you have entry of transaction in your configuration file as below
<tx:annotation-driven />
and also
I hope it will sure help you
Upvotes: 0
Reputation: 26077
Also you can create your own custom exception class and throw it.
Just throw any RuntimeException
from a method marked as @Transactional
.
By default all RuntimeExceptions
rollback transaction whereas checked exceptions don't. This is an EJB legacy. You can configure this by using rollbackFor()
and noRollbackFor()
annotation parameters:
@Transactional(rollbackFor=Exception.class)
This will rollback transaction after throwing any exception.
@Transactional(rollbackFor = MyCheckedException.class)
public void foo() {
throw new RuntimeException();
}
Upvotes: 0