In Spring TransactionSynchronization interface it has methods (in order of execution):
- beforeCommit
- beforeCompletion
- afterCommit: Can perform further operations right after the main transaction has successfully committed.
- afterCompletion
Why Spring doesn't have rollback methods, such as beforeRollback or afterRollback but it has for commit only (beforeCommit and afterCommit)? Will this is necessary? Can anyone give me some advices or explains about this?
If I want to continue further operations that are supposed to follow on a successful rollback of the main transaction, like notification messages or emails what should I do in this case?
#Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
logger.trace("Rolled back...");
}
}
Can I ask why you are using this interface?
This is used for callbacks by the PlatformTransactionManager which is in charge of managing the transaction lifecycle.
The "normal" use of Spring Transactions is to utilise #Transactional annotations, aop declarations in xml or TransactionTemplate / PlatformTransactionManager programatically to set the transaction scope, behaviour and visibility, as described in the docs - here.
To manage a transaction behaviour for rollbacks etc you just tell spring what to do in the event of an Exception
public class Foo {
#Autowired public Service someService;
#Transactional(propagation = Propagation.REQUIRES_NEW,
noRollbackFor = {IOException.class})
public boolean bar(SomeObject someObject) throws IOException {
someService.doComplicatedThing(someObject.getValue());
}
}
This tells the PlatformTransactionManager to start a new TX when hitting the foo() method, commit on successful return or an IOException, and rollback if there is some other type of exception. This is the genius of it - you dont need to worry about littering your code with getTransaction().isActive() and complicated checks for managing the isolation.
Related
It's not exactly as the title says, but close to. Consider these Spring beans:
#Bean
class BeanA {
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = EvilException.class)
public void methodA() {
/* ... some actions */
if (condition) {
throw new EvilException();
}
}
}
#Bean
class BeanB {
#Autowired private BeanA beanA;
final int MAX_TRIES = 3;
#Transactional(propagation = Propagation.NESTED)
public void methodB() {
// prepare to call Bean A
try {
beanA.methodA();
/* maybe do some more things */
}
catch (EvilException e) {
/* recover from evil */
}
}
}
#Bean
class MainWorkerBean {
#Autowired private BeanB beanB;
#Autowired private OtherBean otherBean;
#Transactional(propagation = Propagation.REQUIRED)
public void doSomeWork() {
beanB.methodB();
otherBean.doSomeWork();
}
}
Important note: I'm using JDBC transaction manager that supports savepoints.
What I'm expecting this to do is, when EvilException is thrown, the transaction of the BeanA is rolled back, which with this setup happens to be the savepoint created by starting methodB. However, this appears to not be the case.
When going over with debugging tools, what I'm seeing is this:
When doSomeWork of MainWorkerBean starts, new transaction is created
When methodB starts, transaction manager properly initializes a savepoint and hands it to TransactionInterceptor
When methodA starts, transaction manager sees Propagation.REQUIRED again, and hands out a clean reference to the actual JDBC transaction again, that has no knowledge of the savepoint
This means that when exception is thrown, TransactionStatus::hasSavepoint return false, which leads to roll back of the whole global transaction, so recovery and further steps are as good as lost, but my actual code has no knowledge of the rollback (since I've written recovery for it).
For now, I can't consider changing BeanA's transaction to Propagation.NESTED. Admittedly, looks like it's going to allow me to have the more local rollback, but it's going to be too local, because as I understand it, Spring then will have two savepoints, and only roll back the BeanA savepoint, not BeanB one, as I'd like.
Is there anything else I'm missing, such as a configuration option, that would make internal transaction with Propagation.REQUIRED consider that it is running inside a savepoint, and roll back to savepoint, not the whole thing?
Right now we're using Spring 4.3.24, but I already crawled through their code and can't spot any relevant changes, so I don't think upgrading will help me.
As described in this bug ticket: https://github.com/spring-projects/spring-framework/issues/11234
For spring versions < 5.0, in the situation described, the global transaction is set to 'rollback-only'.
In this transaction I am processing several tasks. If an error should occur during a single task, I do not want the whole transaction to be rolled back, therefore I wrap the task processing in another transaction boundary with a propagation of PROPAGATION_NESTED.
The problem comes when, during task processing, calls are made to existing service methods defined with a transaction boundary of PROPAGATION_REQUIRED. Any runtime exceptions thrown from these methods cause the underlying connection to be marked as rollback-only, rather than respecting the current parent transaction nested propagation.
[...]
As of Spring Framework 5.0, nested transactions resolve their rollback-only status on a rollback to a savepoint, not applying it to the global transaction anymore.
On older versions, the recommended workaround is to switch globalRollbackOnParticipationFailure to false in such scenarios.
However, even for Spring5, I noticed when reproducing the problem, that the nested transaction may be rolled back, including all things done in the catch block of methodB(). So your recover code might not work inside methodB(), depending on what your recovery looks like. If methodA() was not transactional, that would not happen. Just something to watch out for.
Some more details to be found here: https://github.com/spring-projects/spring-framework/issues/8135
We are using Spring and Hibernate in our project. We are using Spring transactional annotations and we understand what it does, but we still have some doubts regarding rollbacks.
Let's say we have the following code.
#Transactional
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Autowired
private AnotherService anotherService;
#Override
public void doSomething() throws Exception {
Person person = new Person();
person.setName("John");
personDao.insert(person);
person.setName("Megan");
//What happens if the following call fails and throws and exception.
personDao.update(person);
// What happens if this other service's call fails?
anotherService.doSomethingElse();
}
}
If either the update or the other service's call fails, what would happen to the insert? Would there be a rollback and the insert would never be executed? Or would the insert still persist in the DB? Do I need to declare the following command in each method for the rollback to be executed.
#Transactional(rollbackFor= Exception.class)
If that is the case, what happens if my service call throws the exception? Would it still works? Thanks for the help. I would really liked to understand the way rollbacks are executed inside a service call.
By default if the exception is checked (extends Exception), the rollback won't happen - the transaction will be committed. Otherwise (if defaults were changed or we're talking about unchecked exceptions), the transaction is rolled back and INSERT/UPDATE statements don't persist anything. BTW, this is described in the JavaDocs to the rollbackFor attribute ;)
If the exception is eventually thrown from your Transactional method it doesn't matter where that exception originated.
If you're not sure about the behaviour, you can always debug Spring's HibernateTransactionManager and its superclasses.
I have a main DB handler method, which calls other methods, which are also working with BD things.
I put #Transactional annotation for the main method, because I want to roll back everything, if something goes wrong.
My question is: should I put this annotation also for the submethods, or it will know that the submethods were called from a method which is transactional.
For example, in the deleting method an exception occurs, how can I make sure that the writing part will be also rollbacked:
#Transactional
public void maintDbTings() {
writing();
deleting();
}
#Transactional //do I need this?
public void writing() {
//no exceptions
}
#Transactional //do I need this?
public void deleting() {
//exception occurs
}
Spring begins a transaction when it encounters a method annotated with #Transactional. The transaction’s scope
covers the execution of that method, the execution of any methods that method invokes, and
so on, until the method returns. Any managed resources that are covered by the configured
PlatformTransactionManager and that you use during the transaction scope participate in the
transaction. For example, if you use the org.springframework.jdbc.datasource.DataSourceTransactionManager, a Connection retrieved from the linked DataSource
participates in the transaction automatically.
The transaction terminates one of two ways: Either the method completes execution directly and the transaction manager commits the transaction, or the method throws an exception and the transaction manager rolls the transaction back.
I hope it is clear now.
In plain english, when you have this:
#Transactional
public void maintDbTings() {
writing();
}
#Transactional //do I need this?
public void writing() {
//no exceptions
}
And call mainDbTings, the #Transactional on the writing has no effect. Meaning that the transaction that was started for mainDbThings will still be present/open in writing. So in this case you can easily drop it.
On the other hand since writing is public someone might call it expecting it to be transactional, since it is a service class most probably. In this case making writing to be #Transactional is mandatory and you can't drop it.
So it's up your needs really.
You can use propagation properties like REQUIRED, REQUIRES_NEW, NESTED according to your requirement as described in the below link:
http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html
I have spring injecting a service into itself to allow the service to make transactional calls to itself. Unfortunately, I'm finding that a requires_new method that is throwing a NullPointerException and being caught is not rolling back the new transaction. The outer transaction is not interrupted which is what I want, but I'm having trouble explaining why the requires new transaction isn't rolled back. Any ideas?
#Service(value="orderService")
#Transactional
public class OrderServiceImpl implements OrderService {
#Resource
private OrderService orderService; // transactional reference to this service
public void requiredTransMethod(){
try {
orderService.requiresNewTransMethod();
}catch(Throwable t){
LOG.error("changes from requiresNewTransMethod call should be rolled back right?", t);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewTransMethod() {
// database changes that are NOT rolled back
throw new NullPointerException("bla bla bla");
}
}
This could be an instance of transactional annotations not working because you are calling them from within the same class.
The way Spring's AOP implementation works (by default) is by using proxy classes, which will not work as expected for method invocations from within the same class.
In Spring how can we make sure that certain operations are always executed together. If any one of them fails, the entire transaction needs to be rolled back. I searched this a lot and found #Transactional(propagation = Propagation.REQUIRED) annotations and TransactionTemplate.execute() methods close to my problem. Kindly clarify and help.
Both #Transactional and TransactionTemplate ensure atomicity. #Transactional is for declarative transaction management, TransactionTemplate is for programmatic transaction management. You should choose one.
The idea of transaction propagation applies only to declarative transaction management and defines a transaction behaviour when it is executed in more than one method. Note that Propagation.REQUIRED is default for Transactional.propagation. It means Support a current transaction (that is if a transaction was already started in the calling method) or create a new one if none exists.
#Transactional(propagation = Propagation.REQUIRED
May solve your problem.
Suppose in your Impl there is a method Excecute.Inside Excecute method there are other M1(),M2(),M3(),M4(),M5() methods.
May be you trying to say if for M1(),M2().M3().M4() methods Db operation succedded and at last for M5() it throws some exception then M1() to M5() all db operation should be rollback
Execute(){
M1();
M2();
M3();
M4();
M5();
if(Any error in any methods transaction will be roll back).As single trasaction object is used for all methods i.e(M1 to M5) when #Transactional(propagation = Propagation.REQUIRED is used.
}
You can create a single method that delegates to the two database calls and annotate that with #Transactional, e.g.
#Transactional
public void atomicOperation(...) {
dbCall();
logicOperation();
}
If either one fails, e.g. an exception is thrown, the entire operation will rollback. The Spring Reference Documentation has dedicated a chapter for transactions, for example there is information about #Transactional settings and Transaction propagation.