Reputation: 93
I'm using spring 4 with spring boot and i need to use @transactional. Correct me if I'm wrong - @transactional make my function run in 1 transaction with the db so if something went wrong the the function will do rollback and nothing will change in the db, else it will do commit.
I need to update 4 rows (from 4 different tables) at the same time, if one of them fails to update I need to rollback. So in my service I have this function:
@Transactional
public void updateDB(entity1, entity2, entity3, entity 4) {
save(entity1);
save(entity2);
save(entity3);
save(entity4);
}
Now I want that my function will return true if the transaction ended with commit, else I want it to return false. Is there any way in doing so?
Upvotes: 3
Views: 2043
Reputation: 952
It is difficult to do this. The actual commit happens after the method under @Transactional returns. For example, some of the checks that the database needs to do are done when the data is written to the table. For example, a constraint violation on the database will only be caught when the data gets written to the disk. By the time that happens, the method has already returned. So even if you return the status from the method, you will have situations when your method returns success while the database has performed a rollback due to some error.
Upvotes: 0
Reputation: 2817
Just for clarity :
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.
Note that you can rollback for Business Exception by using the rollbackFor
property of the @Transactional
, for example :
@Transactionl(rollBackFor=MyBusinessException.class)
public boolean myTransactionalMethod(){
// stuff here
}
For what you want just return true at the end of your method updateDB :
@Transactional
public boolean updateDB(entity1, entity2, entity3, entity 4) {
save(entity1);
save(entity2);
save(entity3);
save(entity4);
return true;
}
if it doesn't return true that means that your transaction is rolled back
PS : Another note from spring documentation
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) does 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 behavior, so you should not rely on this feature in your initialization code (that is, @PostConstruct).
Upvotes: 1
Reputation: 4532
you can implement it in many way a very simple way may be return true at the end in fact only if your method end with the return instruction you can get the result and the transaction manager will garantee you that the transaction is finished and all the save are on the same transaction.
However pleas! pay attention if save(entity) method is of the same class of updateDB(....) the save will be not in transaction even if your class or methods are marked with @Transaction. This is a tricky "feature" of Spring that is correlated to the spring auto proxy. The magical Transaction is fired and propagated because your class is wrapped around to a Proxy that delegate Spring to manage the transaction for you. This is true for teh Spring bean that on the bean creation lifecycle are proxed with the necessary component. However if in your @Transactional class A you call an other method of class A the your service call do not pass from proxy and the second service call is not in transaction.
I give you an example:
@Transactional
class A {
public Entity save(Entity e) {
.....
}
public Entity update(Entity e) {
.....
// the save method is not proxed becouse it is callled from the current instance and not
// form the proxed spring bean instance
save(e);
...
}
}
@Transactional
class SaveA {
public Entity save(Entity e) {
.....
}
}
@Transactional
class UpdateA {
private final SaveA saveA;
UpdateA(SaveA saveA) {
this.saveA = saveA;
}
public Entity update(Entity e) {
.....
// the save method is proxed becouse it is called from saveA that is a proxed bean
saveA.save(e);
...
}
}
Upvotes: 0