Spring Annotation: #Transactional - spring

i want to make somethings clear about #Transactional spring annotation.
Suppose i have written my code something as under,
case-1
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
.....method body....
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
try{
method1()
}
catch(Exce...){
}
}
case 2:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
try{
}
catch(Excep..){
}
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
method1()
}
What would happen in case-1 if some exception occurs in excecution of method1, does it rollback transaction?
and what would happen for the same in case-2.
Thanx in advance

There will be no rollback in any of your use cases regardless of the exception type or propagation as long as you catch the exception in method2.
Why?
Spring creates a proxy class for the Transactional class or members, this is not visible for you. This proxy makes it possible to Spring to execute code before and after your function, basically open transaction, commit or rollback. This proxy is injected into your classes when you autowire them.
Of course the Transactional annotations only work when you're calling them through the proxy!
Your method1 call is always a local function call, so as long as you catch the exception in method2 it won't cause any rollback.
But please read the documentation.

You are free to specify. Using annotations for instance you can write like this:
#Transactional(rollbackFor=Exception.class, timeout=10)
Default behavior is not to rollback when an exception is thrown across the transaction demarcation line.

The Spring #Transactional default configuration (as you used it here) is rolling back only for java.lang.RuntimeException s and the propagation is REQUIRED.
So in case you are calling method2():
CASE 1: no rollback as you are catching in method2() java.lang.Exception (which is the parent of java.lang.RuntimeException)
CASE 2: same - as you are catching in method1() java.lang.Exception
The meaning of the propagation here is that when you've started a transaction if you encounter another annotation of #Transaction another transaction is not started and the application uses the "outer" transaction. So do not expect that the rollback logic will get executed before the method2() finishes. Anyway, one may assume from your snippet that the methods are in the same Spring Bean so calling a method internally in a bean (method2() calls method1() here) the AOP annotation are not "active" meaning that the f/w will not even "see" the #Transactionl on method1().
EDIT
Question changed so that the rollback is for all java.lang.Throwable s. So the answer is the same except for the fact that if an java.lang.Error is thrown then there is a rollback in both cases after the method2() is finished.

Related

Multiple transaction management in Spring JPA

I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
#Transactional(rollbackFor = Exception.class)
public methodA() {
// do some stuff
methodB();
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public methodB() {
try {
methodC();
} catch (Exception e) {
// only log
}
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public methodC() {
// do some stuff
}
What I want to achieve is this: when methodA is called, everything executed before calling methodB should be persisted, independently to what happens afterwards. For this reason I added methodB, the only purpose of which is to create a new transaction, so if the transaction of methodC rollbacks, the original transaction of methodA does not rollback as well. So to sum this up, I want the first transaction to commit and the second to rollback in case of failure. Note that methodC cannot be changed in any way.
This code however does not work and I still get the exception: "TransactionException: Transaction was marked for rollback only; cannot commit"
Any ideas on what I'm doing wrong?
I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
So you're using three methods inside same service
This highlight is important because of how spring AOP works.
If you call method within the same class, then the other methods do not get proxied by #Transaction so they're still using the same transaction of methodA.
Therefore you will see exception when the transaction is marked as rollbackOnly by methodB
So to solve this: You have to move the method methodB to another service so it will proxy-able by Spring.

Grails/Spring REQUIRES_NEW rolling back outer transaction

REQUIRES_NEW is rolling back all transactions:
I have a method marked #Transactional(propagation = REQUIRES_NEW)
within a bean that also has methods marked #Transactional(propagation = REQUIRED). These methods never call each other. They are called from another bean individually. However when my method that has been marked with REQUIRES_NEW fails, it rolls back the whole transaction, including the methods that are marked REQUIRED (which is problematic).
My understanding is that if factored this way Spring AOP will have a chance to intercept the REQUIRES_NEW method and start a new logical transaction.
The general idea looks like this:
#Transactional
class TransactionalBean{
#Transactional(propagation = REQUIRED)
public void methodA(){ //do transactional work }
#Transactional(propagation = REQUIRES_NEW)
public void methodB(){ //do transactional work }
}
And then the calling bean looks like so:
#Transactional
class CallingBean{
#Autowired
TransactionalBean
public void doWork(){
TransactionalBean.methodA();
TransactionalBean.methodB();
}
}
So if method A and B succeed, everything is fine. However when method B fails work done in method A gets rolled back. My understanding is that when methodB() is invoked that it 'should' get intercepted by AOP and start a new transaction and suspend the other transaction, but it's not working. How do I fix this? I'm using Grails 2.5, Hibernate, JPA
The problem is with the way the CallingBean was marked as #Transactional. What is happening is that the CallingBean is creating a transaction, let's say t1. The CallingBean is invoking the two transactional methods methodA and methodB. Since methodA requires a transaction, it will make use of the existing transaction t1. However, methodB will create a new transaction as the annotation requires a new one, let's say t2. The transactional boundary of methodA does not end when the control exits methodA, but is kept alive until methodB is completed since the transaction started at the calling bean. Now that the transaction t2 is failing in methodB, the exception would bubble up to the calling bean and the transaction t1 would fail.
In order to resolve this, you can do one of the following based on your needs.
Turn off the #Transactional annotation in the CallingBean class.
//#Transactional
class CallingBean{
...
}
Update the annotation to use noRollbackFor option
#Transactional(noRollbackFor={WhateverException.class})
class CallingBean{
...
}

Spring nested transactions

In my Spring Boot project I have implemented following service method:
#Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
Inside this method I use another service method which is called update:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
I need to commit changes to database in update method independently of the owner transaction which is started in validateBoard method. Right now any changes is rolling back in case of any exception.
Even with #Transactional(propagation = Propagation.REQUIRES_NEW) it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
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. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
However, there is an option to switch to AspectJ mode
Using "self" inject pattern you can resolve this issue.
sample code like below:
#Service #Transactional
public class YourService {
//... your member
#Autowired
private YourService self; //inject proxy as an instance member variable ;
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
The point is using "self" rather than "this".
The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it.
In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in #Transactional (isolation = "")
You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".
Your code snippet shows that you are explicitly throwing the exceptions.
YOU MUST KNOW::
In its default configuration, 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.
But #Transactional never rolls back a transaction for any checked exception.
Thus, Spring allows you to define
Exception for which transaction should be rollbacked
Exception for which transaction shouldn't be rollbacked
Try annotating your child method: update with #Transactional(no-rollback-for="ExceptionName") or your parent method.
Your transaction annotation in update method will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.
Your problem is a method's call from another method inside the same proxy.It's self-invocation.
In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.
This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.
#Service
class SomeService ..... {
-->> #Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
#Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
#Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
when you do call context.getBean(SomeService.class).methodTwo(object); container returns proxy object and on this proxy you can call methodTwo(...) with transaction.
You could create a new service (CustomTransactionalService) that will run your code in a new transaction :
#Service
public class CustomTransactionalService {
#Transactional(propagation= Propagation.REQUIRES_NEW)
public <U> U runInNewTransaction(final Supplier<U> supplier) {
return supplier.get();
}
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void runInNewTransaction(final Runnable runnable) {
runnable.run();
}
}
And then :
#Service
public class YourService {
#Autowired
private CustomTransactionalService customTransactionalService;
#Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}

Testing #TransactionalEvents and #Rollback

I've been trying to test out #TransactionalEvents (a feature of Spring 4.2 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) with our existing Spring JUnit Tests (run via either #TransactionalTestExecutionListener or subclassing AbstractTransactionalUnit4SpringContextTests but, it seems like there's a forced choice -- either run the test without a #Rollback annotation, or the events don't fire. Has anyone come across a good way to test #TransactionalEvents while being able to #Rollback tests?
Stéphane Nicoll is correct: if the TransactionPhase for your #TransactionalEventListener is set to AFTER_COMMIT, then having a transactional test with automatic rollback semantics doesn't make any sense because the event will never get fired.
In other words, there is no way to have an event fired after a transaction is committed if that transaction is never committed.
So if you really want the event to be fired, you have to let the transaction be committed (e.g., by annotating your test method with #Commit). To clean up after the commit, you should be able to use #Sql in isolated mode to execute cleanup scripts after the transaction has committed. For example, something like the following (untested code) might work for you:
#Transactional
#Commit
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() { /* ... */ }
Regards,
Sam (author of the Spring TestContext Framework)
Marco's solution works but adding REQUIRES_NEW propagation into business code is not always acceptable. This modifies business process behavior.
So we should assume that we can change only test part.
Solutin 1
#TestComponent // can be used with spring boot
public class TestApplicationService {
#Autowired
public MyApplicationService service;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() {
service.doSomething();
}
}
This wraps the real service into test component that can be decorated with REQUIRES_NEW propagation. This solution doesn't modify other logic than testing.
Solution 2
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
TestTransaction.flagForCommit(); //Spring-test since 4.1 - thx for Sam Brannen
TestTransaction.end();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This is simplest solution. We can end test transaction and mark it for commit. This forces transactional events to be handled. If test changes data, cleanup sql script must be specified, otherwise we introduce side effects with committed modified data.
Sam Brannen's solution almost works with regard to adam's comment.
Actually the methods annotated with #TransactionalEventListener are called after the test method transaction is committed. This is so because the calling method, which raises the event, is executing within a logical transaction, not a physical one.
Instead, when the calling method is executed within a new physical transaction, then the methods annotated with #TransactionalEventListener are invoked at the right time, i.e., before the test method transaction is committed.
Also, we don't need #Commit on the test methods, since we actually don't care about these transactions. However, we do need the #Sql(...) statement as explained by Sam Brannen to undo the committed changes of the calling method.
See the small example below.
First the listener that is called when the transaction is committed (default behavior of #TransactionalEventListener):
#Component
public class MyListener {
#TransactionalEventListener
public void when(MyEvent event) {
...
}
}
Then the application service that publishes the event listened to by the above class. Notice that the transactions are configured to be new physical ones each time a method is invoked (see the Spring Framework doc for more details):
#Service
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {
public void doSomething() {
// ...
// publishes an instance of MyEvent
// ...
}
}
Finally the test method as proposed by Sam Brannen but without the #Commitannotation which is not needed at this point:
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This way it works like a charm :-)

Spring Data Jpa - Repository doesn't thrown exception when #transactional exists

I have the following classes:
public interface GarageRepository extends PagingAndSortingRepository<Garage, Integer> {}
public class GarageBO {
private GarageRepository garageRepository;
public void updateGarage(Garage garage) {
try {
garageRepository.save(garage);
} catch (Exception e) {
throw BoozinaExceptions.getCodeException(garage, e);
}
}
}
public class GarageFacade implements GarageService {
private GarageBO garageBO;
#Transactional
public void updateGarage(Garage garage) {
garageBO.updateGarage(garage);
}
}
Supposing that i'm trying to update a garage and an unique violation is throwed.
When i call updateGarage from GarageFacade with the #Transactional annotation, garageRepository doesn't throws any exception.
When i call the same method without the #Transactional annotation, garageRepository throws the unique violation exception and now i can convert the exception using BoozinaExceptions.getCodeException(garage, e);
This behavior happens because when i have the #Transactional annotation, the exception will be throwed when Spring jpa data execute the commit. This happens after GarageBO.updateGarage execution ok ?
But i need to convert the unique violation. How can i do that ?
How to handle spring exception after commit then convert to my exception ?
What you describe is the expected behavior. The transaction is committed after the method ends, the commit leads to the unique constraint violation.
Adding #Transactional to the service methods makes the transaction end after the call to the service method the transaction is committed. Removing it makes the transaction commit after the call to the repository method.
Also why do you have a BO a Facade and a Repository?! Basically the BO and Facade are the same IMHO.
TO fix your your problem have your GarageRepository extend JpaRepository instead of PagingAndSortingRepository and call the saveAndFlush method instead of save. This will execute the sql (not committing the transaction) and trigger a constraint violation exception.
Another solution is to instead of doing a try/catch in your BO create and Aspect which does the conversion. Saves you coding the try/catch each time you need it.

Resources