In adittion to the answers I received, I want to add the following tests I did to be 100% sure I understand the behaviour so here it goes:
The method to test is this one:
@Transactional
public String execute() {
lock();
try {
saveEntities(entities);
doSomethingElse();
return "OK";
} finally {
unlock();
}
}
Test #1:
What happens if everything goes well but at the finally clause, the unlock method fails and throws a RunTimeException()
Answer: It rollbacks the transaction, even more interesting, if you check the database while the finally is executing, you wont see any entities saved yet, it will not save anything until all the function finishes (correctly not abruptly) including the finally.
Test #2: What happens if everything goes well but at the finally clause, the unlock methods fails and throws a RunTimeException(), BUT you catch it there. Answer: It does not rollback the transaction, because since nothing was thrown without being controlled/catched, then everything finishes OK. This example would look like so:
@Transactional
public String execute() {
lock();
try {
saveEntities(entities);
doSomethingElse();
return "OK";
} finally {
try {
unlock();
} catch (RuntimeException e) {
log.info("Catched the problem from finally so no rollback happens");
}
}
}
I did several more test but with this 2, we can conclude that the @Transactional will make sure of 2 things: the code inside the try{} body must execute without any problems (Exceptions/RunTimeExceptions) for it to not rollback, THEN the finally clause code must also execute without any problem for it to not rollback.
If anything goes wrong inside the try{} body, it will already be marked as a Transaction to rollback, no matter what happens in the finally, if it fails inside the try{} body and then it also fails at the finally it will still rollback, even if you catch the problem from the finally, since the try is already finished abrupted/error
So I hope this helps clarify, if you do not use @Transactional it has a completely different behaviour, for example without Transactional if your code in the body of the try{} goes well, you will se the records in your database already, but if the finally goes wrong you will still see them. Another scenario is that if you do not use @Transactional and anything goes wrong in the body of the try{} then you will not see anything in the database (it will "rollback") even if you do not use Transactional, thanks to the Transactional annotations that JpaRepositories already use (Spring), this would be OK if you did not depend on the code that executes on finally, but in my case it is important that this code also succeds.
Also if you are wondering how I managed to see this in the database, I used Thread.sleep after the save method, when catching the Exceptions, etc. That is how I could validate the theory step by step
If you have any doubts feel free to post I will answer, if you imagine any scenario I probably did the test so feel free to ask