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.
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
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
The situation is as follows:
Method1 has four database update methods in it. The Method1 is annotated using the Spring transaction management semantics.
Method2 has a database read method in it and it is invoked after Method1 has finished executing all its database updates. Method2 is also annotated using the Spring transaction semantics.
There's a web request that comes in, the controller intercepts the request and invokes method1 and then method2.
A transaction is wrapped around the web-request as well.
What I am interested in knowing is:
1.How does Spring know to commit the database updates upon a successful transaction? Is there some reference to the Spring implementation that does the transaction management?
2.Since we have a hierarchy of transactions:
Transaction around the web-request->Transaction with Propagation=RequestNew for Method1->Transaction with Propagation=Required for Method2, how does Spring do the transaction management to ensure the transactions are executed within the proper context with the right order?
In short, it will be great to get a play by play account of how Spring performs the transaction management in all its grittiest details or a reference to documentation that doesn't simply hand-wave an explanation centered around JTA or some other acronym.
Thanks
Lets make some basic statements.
A transactional context is an environment where some special properties (database session) are made available to the application runtime which are otherwise not available. A Transaction Context is generally used to scope a transaction.
Spring uses, AOP Proxies and XML metadata to achieve a Declarative transaction management.
Annotations are used to mark the Transaction Propagation behavior of a particular method.
Spring uses Interceptor Mechanism to apply the transaction over the methods.
Here I am reusing the example give by #stacker above
MyClass{
#Transactional
public void sequence() {
method1();
method2();
}
#Transactional
void method1() {
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
void method2() {
}
}
You can also achieve the same functionality using xml configuration as well. Lets take this as its popular and widely used.
At the time of deployment
Spring framework checks the xml configuration files (famed applicationContext.xml) and depending on the configuration, scans the code for #Transactional annotation (assuming that the configuration is mentioned as annotation based).
After this, it generates AOP proxies for methods marked for transaction. In simple terms, these proxies are nothing but wrapper around the concerned methods.
Within these wrapper methods, before and after a Transaction Advisor code is also generated depending on the configuration (namely the transaction propagation).
Now when these wrapper methods are invoked Transaction Advisor comes into picture before and after the actual method call. .
Representing the same in pseudo code for the example above
ProxyMyClass{
MyClass myclass;
.
.
.
sequence(){
//Transaction Advisor code (Typically begin/check for transaction)
myclass.sequence();
//Transaction Advisor code(Typically rollback/commit)
}
.
.
.
}
This is how spring managers the transaction. A slight oversimplification though.
Now to answer your questions,
.How does Spring know to commit the database updates upon a successful transaction? Is there some reference to the Spring implementation that does the transaction management?
Whenever you call a method under transaction, you actually call a proxy which first executes the transaction advisor (which will begin the transaction), then you call the actual business method, once that completes, another transaction advisor executes (which depending on way method returned, will commit or rollback transaction).
Since we have a hierarchy of transactions: Transaction around the web-request->Transaction with Propagation=RequestNew for Method1->Transaction with Propagation=Required for Method2, how does Spring do the transaction management to ensure the transactions are executed within the proper context with the right order?
In case of transaction hierarchy, the spring framework generates the Transaction Advisor checks accordingly. For the example you mentioned,
for method1 (RequestNew ) Transaction Advsor code (or transaction Advice) would be to create a new transaction always.
for method2 (Required ) Transaction Advisor code (or transaction Advice) would be check for existing transaction and use the same if it exists or else create a new transaction.
There is an image on the spring documentation page which very nicely summarizes these aspects.
Hope this helps.
Controller
#Transactional
public void sequence() {
method1();
method2();
}
#Transactional
void method1() {
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
void method2() {
}
The default propagation is REQUIRED (Support a current transaction, create a new one if none exists.) Therefore m1 will use the Transaction started in the Controller. m2 is annotated as REQUIRES_NEW ( Create a new transaction, suspend the current transaction if one exists.) The order of the transaction is the order you call the transactional methods.
Controller
begin tx1
|--------------------> m1 (uses tx1)
|
| begin tx2
|--------------------> m2 (uses tx2)
| commit tx2
commit tx1
Have you read the Spring documentation? Basically AOP is used to manage the transaction. You should also read the AOP documentation. If the AOP documentation is not enough I suggest you go through the code. Stepping through the code in debug mode with break-point would be good.
I've seen a method in a Service class that was marked as #Transactional, but it was also calling some other methods in that same class which were not marked as #Transactional.
Does it mean that the call to separate methods are causing the application to open separate connections to DB or suspend the parent transaction, etc?
What's the default behavior for a method without any annotations which is called by another method with #Transactional annotation?
When you call a method without #Transactional within a transaction block, the parent transaction will continue to the new method. It will use the same connection from the parent method (with #Transactional) and any exception caused in the called method (without #Transactional) will cause the transaction to rollback as configured in the transaction definition.
If you call a method with a #Transactional annotation from a method with #Transactional belonging to the same Spring Bean, then the called methods transactional behavior will not have any impact on the transaction. But if you call a method with a transaction definition from another method with a transaction definition, and they belong to different Spring Beans, then the code in the called method will follow its own transaction definitions.
You can find more details in the section Declarative transaction management of spring transaction documentation.
Spring declarative transaction model uses AOP proxy, so the AOP proxy is responsible for transactions creation. The AOP proxy will be active only if the called method belong to a different Spring Bean than the caller one.
Does that mean the call to separate methods are causing the application to open separate connections to DB or suspend the parent transaction, etc?
That depends on a propagation level. Here are all the possible level values.
For example in case a propagation level is NESTED a current transaction will "suspend" and a new transaction will be created ( note: actual creation of a nested transaction will only work on specific transaction managers )
What's the default behavior for a method without any annotations that is called by another method with #Transactional annotation?
The default propagation level ( what you call "behavior" ) is REQUIRED. In case an "inner" method is called that has a #Transactional annotation on it ( or transacted declaratively via XML ), it will execute within the same transaction, e.g. "nothing new" is created.
#Transactional marks the transaction boundary (begin/end) but the transaction itself is bound to the thread. Once a transaction starts it propagates across method calls until the original method returns and the transaction commits/rolls back.
If another method is called that has a #Transactional annotation then the propagation depends on the propagation attribute of that annotation.
The inner method will affect the outer method if the inner method is not annotated with #Transactional.
In case inner method is also annotated with #Transactional with REQUIRES_NEW, following will happen.
...
#Autowired
private TestDAO testDAO;
#Autowired
private SomeBean someBean;
#Override
#Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
testDAO.insertUser(user);
try{
someBean.innerMethod();
} catch(RuntimeException e){
// handle exception
}
}
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
throw new RuntimeException("Rollback this transaction!");
}
The inner method is annotated with REQUIRES_NEW and throws a RuntimeException so it will set its transaction to rollback but WILL NOT EFFECT the outer transaction. The outer transaction is PAUSED when the inner transaction starts and then RESUMES AFTER the inner transaction is concluded. They run independently of each other so the outer transaction MAY commit successfully.
All I know about this exception is from Spring's documentation and some forum posts with frostrated developers pasting huge stack traces, and no replies.
From Spring's documentation:
Thrown when an attempt to commit a transaction resulted in an unexpected rollback
I want to understand once and for all
Exactly what causes it?
Where did the rollback occur? in the App Server code or in the Database?
Was it caused due to a specific underlying exception (e.g. something from java.sql.*)?
Is it related to Hibernate? Is it related to Spring Transaction Manager (non JTA in my case)?
How to avoid it? is there any best practice to avoid it?
How to debug it? it seems to be hard to reproduce, any proven ways to troubleshoot it?
I found this to be answering the rest of question: https://jira.springsource.org/browse/SPR-3452
I guess we need to differentiate
between 'logical' transaction scopes
and 'physical' transactions here...
What PROPAGATION_REQUIRED creates is a
logical transaction scope for each
method that it gets applied to. Each
such logical transaction scope can
individually decide on rollback-only
status, with an outer transaction
scope being logically independent from
the inner transaction scope. Of
course, in case of standard
PROPAGATION_REQUIRED behavior, they
will be mapped to the same physical
transaction. So a rollback-only marker
set in the inner transaction scope
does affect the outer transaction's
chance to actually commit. However,
since the outer transaction scope did
not decide on a rollback itself, the
rollback (silently triggered by the
inner transaction scope) comes
unexpected at that level - which is
why an UnexpectedRollbackException
gets thrown.
PROPAGATION_REQUIRES_NEW, in contrast,
uses a completely independent
transaction for each affected
transaction scope. In that case, the
underlying physical transactions will
be different and hence can commit or
rollback independently, with an outer
transaction not affected by an inner
transaction's rollback status.
PROPAGATION_NESTED is different again
in that it uses a single physical
transaction with multiple savepoints
that it can roll back to. Such partial
rollbacks allow an inner transaction
scope to trigger a rollback for its
scope, with the outer transaction
being able to continue the physical
transaction despite some operations
having been rolled back. This is
typically mapped onto JDBC savepoints,
so will only work with JDBC resource
transactions (Spring's
DataSourceTransactionManager).
To complete the discussion:
UnexpectedRollbackException may also
be thrown without the application ever
having set a rollback-only marker
itself. Instead, the transaction
infrastructure may have decided that
the only possible outcome is a
rollback, due to constraints in the
current transaction state. This is
particularly relevant with XA
transactions.
As I suggested above, throwing an
exception at the inner transaction
scope, then catching that exception at
the outer scope and translating it
into a silent setRollbackOnly call
there should work for your scenario. A
caller of the outer transaction will
never see an exception then. Since you
only worry about such silent rollbacks
because of special requirements
imposed by a caller, I would even
argue that the correct architectural
solution is to use exceptions within
the service layer, and to translate
those exceptions into silent rollbacks
at the service facade level (right
before returning to that special
caller).
Since your problem is possibly not
only about rollback exceptions, but
rather about any exceptions thrown
from your service layer, you could
even use standard exception-driven
rollbacks all the way throughout you
service layer, and then catch and log
such exceptions once the transaction
has already completed, in some
adapting service facade that
translates your service layer's
exceptions into UI-specific error
states.
Juergen
Scroll a little more back in the log (or increase it's buffer-size) and you will see what exactly caused the exception.
If it happens not to be there, check the getMostSpecificCause() and getRootCause() methods of UnexpectedRollbackException- they might be useful.
Well I can tell you how to reproduce the UnexpectedRollbackException. I was working on my project, and I got this UnexpectedRollbackException in following situation. I am having controller, service and dao layers in my project.
What I did is in my service layer class,
class SomeServiceClass {
void outerTransaction() {
// some lines of code
innerTransaction();
//AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass.
}
void innerTransaction() {
try {
// someDaoMethod or someDaoOperation that throws exception
} catch(Exception e) {
// when exception is caught, transaction is rolled back, outer transaction does not know about it.
// I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true)
// FYI : use of following second dao access is wrong,
try {
// again some dao operation
} catch(Exception e1) {
throw e2;
}
}
}
}
I had this problem with Spring Framework 4 + Java 1.8 for this exception.
I solved it.
😊
I know that runtime exceptions rollbacks.
But in my case, project do not use runtime exceptions.
For example, we have code.
#Transactional
#Service
UserFacadeIml implements UserFacade
{
#Autowired
private UserService userService;
//throws UnexpectedRollbackException
//instead NotRuntimeEx
#Override
public void saveUser(User user)
throws NotRuntimeEx
{
return userService.saveUser(user);
}
}
I solved it, when add "Transactional(rollBackFor=NotRuntimeEx.class)" for facade.
It works, because Spring use proxy for facade and proxy for service.
When transactional rollback, proxy of service send context to facade.
But facade proxy does not understand, why transaction rollback.
#Transactional(rollBackFor=NotRuntimeEx.class)
#Service
UserServiceIml implements UserService
{
#Override
public void saveUser(User user)
throws NotRuntimeEx
{
throw new NotRuntimeEx(user);
}
}