Propagation behaviour of transaction - java

I am using annotation based declarative approach for spring aop.
sample code
ClassA{
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
add()
{
method1();
method2();
method3();
}
}
But I have still doubt on use of propagation.does propagation.Requires_New means that each request will start new transaction.
Second question:
Does failure of any method like method2,method3 will cause the transaction to rollback?
I will be very happy if any can help me to leans transaction propagation.
can someone provide me a real world example where we need a participate in existing transaction.because I visualise that add function that we are using in above example will be independent for all users,or any other function will be independent to each user who is calling. I am not able to find example where other propagation behaviour like PROPAGATION_SUPPORTS ,PROPAGATION_MANDATORY,PROPAGATION_REQUIRES_NEW etc. are used

Answering this comment, not the actual question:
transaction are session specific or
request specific – Vish 3 hours ago
Neither. request and session are both web-specific scopes, while the Spring Transaction abstraction has nothing to do with web technologies.
The scope of #Transactional is per method invocation, as #Transactional is implemented through Spring AOP. The transactional state is kept in ThreadLocal variables which are initialized when the outermost #Transactional method is entered and cleared with commit or rollback when it is left. This whole abstraction works on Java method level, and hence does not require or profit from a web container.
And in response to this Question in the comment below:
thanks #sean,i am stil not able to get
answer where other propagation
behaviour like PROPAGATION_SUPPORTS
,PROPAGATION_MANDATORY,PROPAGATION_REQUIRES_NEW
etc are used. please refer above for
whole question
Here's the list of Propagation values with my comments:
MANDATORY
Support a current transaction, throw an exception if
none exists.
Does not start a new Transaction, just checks whether a transaction is active (must be inside either another #Transactional method call or a programmatically created transaction)
NESTED
Execute within a nested transaction if a current transaction
exists, behave like
PROPAGATION_REQUIRED else.
Start a nested transaction if a transaction exists, start a new transaction otherwise.
NEVER
Execute non-transactionally, throw an exception if a transaction
exists.
Does not start a transaction. Fails if a transaction is present.
NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one
exists.
Does not start a transaction. Suspends any existing transaction.
REQUIRED
Support a current transaction, create a new one if none
exists.
If a transaction exists, use that, if not, create a new one. In 95% of cases, this is what you need.
REQUIRES_NEW
Create a new transaction, suspend the current transaction if one
exists.
Always creates a new transaction, no matter if an existing transaction is present. If there is, it will be suspended for the duration of this method execution.
SUPPORTS
Support a current transaction, execute
non-transactionally if none exists.
Can use a transaction if one is present, but doesn't need one (and won't start a new one either)
In most cases, REQUIRED is what you need (hence it's the default in the #Transactional annotation). I have personally never seen any other value but REQUIRED and REQUIRES_NEW in use.

Transaction propagation indicates what should be the behaviour of the given method when it is invoked. REQUIRES_NEW means that a new transaction should always be started, even if there is an ongoing transaction.
If method1(), for example, defines REQUIRES_NEW, than it will execute in a new transaction.
An exception will rollback the current active transaction, yes.

Yes. Requires_New means that each request will start new transaction. and Yes failure in method2,method3 will cause the transaction to rollback, depending on the rollback properties.
check Transactional properties.

Related

What is a transaction boundary in hibernate

I have 2 questions related to each other
Q1 What exactly is a transaction boundary in hibernate/Spring Data JPA.
I am new to JPA , so please give a very basic example so I can understand as I tried to read multiple blogs but still not very clear.
Q2 And on top of it, what does this mean-
In hibernate, persist() method guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries, save() method does not guarantee the same.
What is outside and inside of a transaction boundary and how executions are performed outside boundaries?
A transaction is a unit of work that is either executed completely or not at all.
Transactions are fairly simple to use in a typical relational database.
You start a transaction by modifying some data. Every modification starts a transaction, you typically can't avoid it. You end the transaction by executing a commit or rollback.
Before your transaction is finished your changes can't be seen in other transactions (there are exceptions, variations and details). If you rollback your transaction all your changes in the database are undone.
If you commit your changes your changes become visible to other transactions, i.e. for other users connected to the same database. Implementations vary among many other things if changes become visible only for new transactions or also for already running transactions.
A transaction in JPA is a database transaction plus additional stuff.
You can start and end a transaction by getting a Transaction object and calling methods on it. But nobody does that anymore since it is error prone. Instead you annotate methods with #Transaction and entering the method will start a transaction and exiting the method will end the transaction.
The details are taken care of by Spring.
The tricky part with JPAs transactions is that within a JPA transaction JPA might (and will) choose to delay or even avoid read and write operations as long as possible. For example when you load an entity, and load it again in the same JPA transaction, JPA won't load it from the database but return the same instance it returned during the first load operation. If you want to learn more about this I recommend looking into JPAs first level cache.
A transaction boundary it's where the transaction starts or is committed/rollbacked.
When a transaction is started, the transaction context is bound to the current thread. So regardless of how many endpoints and channels you have in your Message flow your transaction context will be preserved as long as you are ensuring that the flow continues on the same thread. As soon as you break it by introducing a Pollable Channel or Executor Channel or initiate a new thread manually in some service, the Transactional boundary will be broken as well.
some other people ask about it - look it up.
If you do not understand something write to me again more accurately and I will explain.
I really hope I helped!

Will a TransactionAttributeType.REQUIRES_NEW annotated method run on a new Thread?

According to documentation, in container managed transactions, if a method is annotated with TransactionAttributeType.REQUIRES_NEW attribute, will suspend any client transaction, delegate the call to this method/ create a new transaction, and resume to previous transactions after the new one has been completed.
So this actually means, that no new thread is created, and that the previous transaction is on a "wait" state until the new one is has been finished?
As stated in the Java-Doc:
If a client calls with a transaction context, the container suspends the association of the transaction context with the current thread before starting the new transaction and invoking the method. The container resumes the suspended transaction association after the method and the new transaction have been completed.
It's not abandoning the thread or creating a new one, it's only releasing the association to the transaction and recreating it afterwards. If it would create a new thread, there would be no need to remove the association because it was never made.
Container managed transactions means JTA and JTA specifications does not allow transactions to span across multiple threads. Each JTA transaction is associated with an execution thread, so it means that at most one transaction can be active at any given time. Note that multiple transactions can be associated with a single thread, but again only one can ever be active at a given time.
As JTA does not support nested transactions, it means that if one transaction is active, it is not possible to start another one in the same thread, until the first transaction commits or rolled back (or timed out, causing again rollback), thus releasing transaction association with the current thread.
What happens behind the scenes when method with transactional attribute REQUIRES_NEW is called with the transactional context? First, JTA temporarily suspends the transaction that is currently associated with the calling thread by calling it is internal API, particularly it calls TransactionManager.suspend(). (If calling thread is not associated with any transaction (i.e. method was called with transactional attribute of NOT_SUPPORTED or without transactional context) a null object reference is returned) and obtaining Transaction object. This Transaction object will be passed to TransactionManager.resume() method to re-associate the transaction context with the calling thread once above mentioned REQUIRES_NEW method completes. Whether on same or another thread depends on JTA implementation, as specification does explicitly require it to be one way or the other.
Answering your question - the only way for JTA to fulfill transactional attribute REQUIRES_NEW when called in transactional context is to suspend the transaction from a thread and resume it later on the same or another thread. When it will be resumed? Remember that REQUIRES_NEW attribute basically means is that annotated method always have to be in its OWN TRANSACTION, which in turn means that the method should be committed or rolled back independently of methods further up the call stack. Suspended transaction will be resumed, once called method with transactional attribute REQUIRES_NEW commits or rolled back.
As a side notes, as you may probably know, there is no way for Bean Managed Transaction to suspend a transaction, you can't do it programatically. Only JTA can do this using an it's internal transaction management API and you can achieve this declaratively by using CMT and transaction attributes. Also note that this attribute can lead to excessive transaction overheads is overused.

Does it matter where I place noRollbackFor?

In Spring, if I have:
ServiceA.serviceA() -> ServiceB.serviceB() -> ServiceC.serviceC() ->ServiceD.serviceD()
where ServiceD.serviceD() can throw a runtime exception: MyRuntimeException, which is propagated back up to ServiceA.serviceA catch block. Does it matter on which service I put #Transactional(noRollbackFor=[MyRuntimeException.class]) on?
Is there any difference between putting it on any of the services?
Note: All my Services are marked as #Transactional
As you did not give precision on that, I assume that you are using default propagation of PROPAGATION_REQUIRED. In that context, the 4 services will use the same transaction, and if any of the three inner marks the transaction as read-only as a result of the exception, the outer will get a UnexpectedRollbackException to inform it that the asked commit actually resulted in a rollback. From Spring Reference Manual : However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.. And if the outer transaction decides to rollback the transaction because of the exception, the transaction will obviously be rolled back.
So if none of the services catches the exception, and if you use a propagation of PROPAGATION_REQUIRED, at least the four involved methods have to be annotated with #Transactional(noRollbackFor=[MyRuntimeException.class]).
An alternative of using noRollbackFor=[MyRuntimeException.class] would be to catch the exception in the appropriate method of ServiceD. In that case, the exception will never climb up the stack, and none of the transactional proxies will ever knows it occurred. The commit would then normally happen at the end of the transaction.
Edit per comment :
If you want further control on exception management, you could try to duplicate method : a transactional method, that calls a non transactional one in your service class. And you could call the non-transactional one if you do not want another transactional proxy in the chain. But this has sense only if this use case (a service class calling another service class with special exception requirement) is exceptional.
As an alternative, you could inject the implementations on the other service classes instead of injecting the transactional proxies (#Autowired private ServiceBImpl serviceB;). As you are already have a transaction obtained at the outer level, all DAO operations should be fine, and as there is only one transactional proxy at the outer level, you have one single point of control for exception management. It is rather uncommon to inject classes instead of interfaces, and you should document the why in a red flashing font :-) , but it should meet your requirements.
If you use a combination of
#Transactional(propagation = REQUIRES_NEW, noRollbackFor = MyRuntimeException.class)
then this service can commit independently from the rollback of the parent transaction (which will occur if it is not annotated with noRollbackFor). Otherwise you would have to mark all the transactional methods involved with the same noRollbackFor annotation.

Spring transaction REQUIRED vs REQUIRES_NEW : Rollback Transaction

I have a method that has the propagation = Propagation.REQUIRES_NEW transactional property:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void createUser(final UserBean userBean) {
//Some logic here that requires modification in DB
}
This method can be called multiple times simultaneously, and for every transaction if an error occurs than it's rolled back (independently from the other transactions).
The problem is that this might force Spring to create multiple transactions, even if another one is available, and may cause some performance problems.
Java doc of propagation = Propagation.REQUIRED says: Support a current transaction, create a new one if none exists.
This seems to solve the performance problem, doesn't it?
What about the rollback issue ? What if a new method call rolls back while using an existing transaction ? won't that rollback the whole transaction even the previous calls ?
[EDIT]
I guess my question wasn't clear enough:
We have hundreds of clients connected to our server.
For each client we naturally need to send a feedback about the transaction (OK or exception -> rollback).
My question is: if I use REQUIRED, does it mean only one transaction is used, and if the 100th client encounters a problem the 1st client's transaction will rollback as well ?
Using REQUIRES_NEW is only relevant when the method is invoked from a transactional context; when the method is invoked from a non-transactional context, it will behave exactly as REQUIRED - it will create a new transaction.
That does not mean that there will only be one single transaction for all your clients - each client will start from a non-transactional context, and as soon as the the request processing will hit a #Transactional, it will create a new transaction.
So, with that in mind, if using REQUIRES_NEW makes sense for the semantics of that operation - than I wouldn't worry about performance - this would textbook premature optimization - I would rather stress correctness and data integrity and worry about performance once performance metrics have been collected, and not before.
On rollback - using REQUIRES_NEW will force the start of a new transaction, and so an exception will rollback that transaction. If there is also another transaction that was executing as well - that will or will not be rolled back depending on if the exception bubbles up the stack or is caught - your choice, based on the specifics of the operations.
Also, for a more in-depth discussion on transactional strategies and rollback, I would recommend: «Transaction strategies: Understanding transaction pitfalls», Mark Richards.
If you really need to do it in separate transaction you need to use REQUIRES_NEW and live with the performance overhead. Watch out for dead locks.
I'd rather do it the other way:
Validate data on Java side.
Run everyting in one transaction.
If anything goes wrong on DB side -> it's a major error of DB or validation design. Rollback everything and throw critical top level error.
Write good unit tests.

PROPAGATION_REQUIRED transaction attribute in spring?

In first case study given at http://www.vermatech.com/code/SpringTransactionExamples.html,
program is calling two methods i.e.
testModel.deleteAllCountries();
testModel.initializeCountries();
where initializeCountries throws runtime exception. For both methods' transaction definition attribute is PROPAGATION_REQUIRED. Still transaction under deleteAllCountries
method gets committed but transaction under initializeCountries are rolled back(as per the logs given in the same case study).
As per PROPAGATION_REQUIRED definition is that it Support a current transaction; create a new one if none exists. So my question here is transaction under initializeCountries method
should support the transaction under deleteAllCountries method. I mean both method should be treated as single transaction. As per my understanding either complete transaction should be committed or rolled back? Not sure how come logs are treating them separately.
"Propagation required" is defined as
Support a current transaction, create a new one if none exists.
In your case above the deleteAllCountries method executes in a transaction and commits. There is no current transaction when initializeCountries is called, so it executes in a second transaction, and rolling it back has no effect on the changes made by the first method.
Propagation applies to nested method calls, not to successive ones. If you look at the documentation:
When the propagation setting is PROPAGATION_REQUIRED, a logical
transaction scope is created for each method upon which the setting is
applied. Each such logical transaction scope can determine
rollback-only status individually, with an outer transaction scope
being logically independent from the inner transaction scope. Of
course, in case of standard PROPAGATION_REQUIRED behavior, all these
scopes 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 (as you would expect
it to).
However, in the case where an inner transaction scope sets the
rollback-only marker, the outer transaction has not decided on the
rollback itself, and so the rollback (silently triggered by the inner
transaction scope) is unexpected. A corresponding
UnexpectedRollbackException is thrown at that point. This is expected
behavior so that the caller of a transaction can never be misled to
assume that a commit was performed when it really was not. So if an
inner transaction (of which the outer caller is not aware) silently
marks a transaction as rollback-only, the outer caller still calls
commit. The outer caller needs to receive an
UnexpectedRollbackException to indicate clearly that a rollback was
performed instead.
then you can see all of this is about inner- and outer-, none of it mentions successive calls. In your case the call to deleteAllCountries is the outermost transactional method, so when it finishes successfully then Spring commits the transaction immediately. Then your call to initializeCountries has to be executed within a separate transaction, where it is the outermost method.
Your assumption seems to be that Spring will hold the transaction open after the first method finishes, but that's not how it works. In order to get the effect you want, you could create another method on testModel that wraps the calls to deleteAllCountries and initializeCountries, make that method transactional and give it the attribute PROPAGATION_REQUIRED. That way a rollback of the second method will cause the first method's changes to be rolled back too, because the wrapping method is grouping them together. Otherwise nothing is telling Spring these things should be part of the same transaction.

Categories

Resources