Propagation required clariification spring - spring

wanted to clarify the Propagation required fundamental with below scenarios. . Please let me know if below understanding is correct.
Class MyClass1{
//Propagation is Propagation required
public void method1();
{
method1A();
method1B();
MyClass2 myClass2= new MyClass2();
myClass2.method2A();
myClass2.method2B();
}
// No porapgation is defined here so default will be reuired
public method1A()
{
//Some Transaction
}
// No porapgation is defined here so default will be reuired
private method1B()
{
//Some Transaction
}
}
Class MyClass2{
//Propagation is Propagation required
public void method2()
{
method2A();
method2B();
}
// No porapgation is defined here so default will be required
public method2A()
{
//Some Transaction
}
// No porapgation is defined here so default will be required
public method2B()
{
//Some Transaction
}
}
Now here are the scenarios
we call the method1() of MyClass1 inside main method
Scenarion1:-
No exception occurs. transaction will be created before method1A() and will be commited after myClass2.method2B();
Scenarion2:-
Runtime exception occurs during method1B. Complete transaction will be rolled back
Scenarion3:-
Runtime exception occurs during method2A(Transaction under method2A will be treated as part of transaction created
under method1 in class1) .Complete transaction will be rolled back
Scenarion4:-
Runtime exception occurs during method2B(Transaction under method2A will be treated as part of transaction created
under method1 in class1) .Complete transaction will be rolled back
Edit:-
Now if we consider the same scenarios with propagation as Nested for methods method2A and method2B.
Scenarion1:-
No exception occurs. transaction will be created on entering method1A() and will be commited on exit of method1A()
Scenarion2:-
Runtime exception occurs during method1B. Complete transaction will be rolled back
Scenarion3:-
Runtime exception occurs during method2A .Only transaction under method2A will be rolled back and rest of the transaction will be commited
on exit of method1
Scenarion4:-
Runtime exception occurs during method2B. Only transaction under method2B will be rolled back and rest of the transaction will be commited
on exit of method1

Your understanding is (generally) correct, but your example is flawed. By calling:
MyClass2 myClass2= new MyClass2();
You've ensured that method calls on myClass2 will not be intercepted by the transactional proxy, and therefore any propagation required semantics implied here don't really matter since they won't be applied. In this case, however, you will fall within the transactional boundaries of method1 and since you've marked it as propagation required, your code will execute as you've described. You would do well to come up with a SSCCE if you require further clarification.
Also, the Spring documentation on Transaction Management is some of the best you'll find, I highly recommend you take a look at it.

Related

Repository is not saving data onError method, while saving onWrite method of Listener

I have a simple listener with 3 methods. and a repository with autowired on that. While saving an object from afterWrite it works nicely. but when saving item from onError methods no exception occurs, however it is not saving any data. Thankful for suggestions.
public class WriteListener implements ItemWriteListener{
public void beforeWrite(List items) {
System.out.println("Going to write following items: "+ items.toString());
}
public void onWriteError(Exception exception, List items) {
System.out.println("Error occurred when writing items!");
testRepository.save(items.get(0)); //not working
}
public void afterWrite(List items) {
testRepository.save(items.get(0)); //not nicely and save data
Based on the limited information provided, most likely the cause is the exception itself. The exception would have marked current transaction as dirty thus spring would have rolled it back.
If you still want to store data in your listener despite existing exception, use it in a separate transaction context. Simplest way for that would be to use #Async annotation on your listener and marking it Transactional explicitly to ensure it initiate a new transaction. Check out Spring Event which covers this topic in little bit more depth.

Multiple transaction management in Spring JPA

I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
#Transactional(rollbackFor = Exception.class)
public methodA() {
// do some stuff
methodB();
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public methodB() {
try {
methodC();
} catch (Exception e) {
// only log
}
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public methodC() {
// do some stuff
}
What I want to achieve is this: when methodA is called, everything executed before calling methodB should be persisted, independently to what happens afterwards. For this reason I added methodB, the only purpose of which is to create a new transaction, so if the transaction of methodC rollbacks, the original transaction of methodA does not rollback as well. So to sum this up, I want the first transaction to commit and the second to rollback in case of failure. Note that methodC cannot be changed in any way.
This code however does not work and I still get the exception: "TransactionException: Transaction was marked for rollback only; cannot commit"
Any ideas on what I'm doing wrong?
I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
So you're using three methods inside same service
This highlight is important because of how spring AOP works.
If you call method within the same class, then the other methods do not get proxied by #Transaction so they're still using the same transaction of methodA.
Therefore you will see exception when the transaction is marked as rollbackOnly by methodB
So to solve this: You have to move the method methodB to another service so it will proxy-able by Spring.

Spring Annotation: #Transactional

i want to make somethings clear about #Transactional spring annotation.
Suppose i have written my code something as under,
case-1
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
.....method body....
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
try{
method1()
}
catch(Exce...){
}
}
case 2:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
try{
}
catch(Excep..){
}
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
method1()
}
What would happen in case-1 if some exception occurs in excecution of method1, does it rollback transaction?
and what would happen for the same in case-2.
Thanx in advance
There will be no rollback in any of your use cases regardless of the exception type or propagation as long as you catch the exception in method2.
Why?
Spring creates a proxy class for the Transactional class or members, this is not visible for you. This proxy makes it possible to Spring to execute code before and after your function, basically open transaction, commit or rollback. This proxy is injected into your classes when you autowire them.
Of course the Transactional annotations only work when you're calling them through the proxy!
Your method1 call is always a local function call, so as long as you catch the exception in method2 it won't cause any rollback.
But please read the documentation.
You are free to specify. Using annotations for instance you can write like this:
#Transactional(rollbackFor=Exception.class, timeout=10)
Default behavior is not to rollback when an exception is thrown across the transaction demarcation line.
The Spring #Transactional default configuration (as you used it here) is rolling back only for java.lang.RuntimeException s and the propagation is REQUIRED.
So in case you are calling method2():
CASE 1: no rollback as you are catching in method2() java.lang.Exception (which is the parent of java.lang.RuntimeException)
CASE 2: same - as you are catching in method1() java.lang.Exception
The meaning of the propagation here is that when you've started a transaction if you encounter another annotation of #Transaction another transaction is not started and the application uses the "outer" transaction. So do not expect that the rollback logic will get executed before the method2() finishes. Anyway, one may assume from your snippet that the methods are in the same Spring Bean so calling a method internally in a bean (method2() calls method1() here) the AOP annotation are not "active" meaning that the f/w will not even "see" the #Transactionl on method1().
EDIT
Question changed so that the rollback is for all java.lang.Throwable s. So the answer is the same except for the fact that if an java.lang.Error is thrown then there is a rollback in both cases after the method2() is finished.

Spring + Hibernate how to prevent rollback

I have a code block with this method:
#Transactional(noRollbackFor=Exception.class)
public synchronized Object saveData(){
//...
dao.insert(entity);
//...
}
My dao class is marked ad Transactional
#Transactional
public class Dao {
//...
public <T> void insert(T obj) throws Exception {
getSession().saveOrUpdate(obj);
}
}
I need to prevent any rollback inside this block. However I got this:
2014-02-25 20:47:44 [WARN]SqlExceptionHelper:143 SQL Error: 1205, SQLState: 41000
2014-02-25 20:47:44 [ERROR]SqlExceptionHelper:144 Lock wait timeout exceeded; try restarting transaction
2014-02-25 20:47:44 [ERROR]BigliettiService:? Transazione in errore
org.hibernate.exception.GenericJDBCException: Lock wait timeout exceeded; try restarting transaction
...
2014-02-25 20:47:44 [ERROR]JsonHandlerExceptionResolver:? Transaction rolled back because it has been marked as rollback-only
Why at the end I found a Transaction rolled back?
I had a similar issue just last week. In my case it was another #Transactional annotation from a different method that caused the transaction to be marked as rollback-only.
Did you check for that yet?
Edit: To clarify it a bit more, you didn't post your code from inside your saveData() method. What caused this error for me was calling another method with #Transactional (lacking the noRollback attribute) from inside my method, in your case saveData().
I don't think you can do that. Your transaction is being propagated by default to the inner method and if this method rollbacks your transaction, the outer method can only be notified by a UnexpectedRollbackException that the rollback already happened.. and acknowledge it.
You can change the propagation config to start a new inner transaction but this won't help as it will still be commited independently before reaching the outer transaction.
If you can't set a NoRollback attribute at DAO level, I guess the best solution would be to remove the Transactional attributes from your DAOs and have a layer of facade services calling these DAOs and defining their own rollback strategies.

Exception propagates after already caught

I'm having the strangest thing happening and I can't figure out why. The best way to describe this is to provide a simplistic example:
#Service
#Transactional
public class Foo{
public ModelAndView delete(#ModelAttribute("abc") Long id) {
ModelAndView mav = new ModelAndView();
try {
getDaoService().delete(id); //Calls Bar.delete()
} catch (final Exception e) {
// Add a custom error message to the mav for the user to see
mav.getModelMap().addAttribute(blah, blah);
}
return mav;
}
}
#Service
#Transactional
public class Bar {
public void delete(final E entity) throws HibernateException {
if (null != entity) {
try {
sessionFactory.getCurrentSession().delete(entity);
} finally {
sessionFactory.getCurrentSession().flush();
}
}
}
}
In this particular case, I am trying to delete an object which has a constraint violation (ORA-02292). I expect the delete to fail because of this. When the delete fails, I wish to show the user an appropriate custom message.
Instead of being able to show the user a custom message, the call fails and displays the following to the screen:
org.springframework.transaction.UnexpectedRollbackException: Transaction
rolled back because it has been marked as rollback-only
When I use a debugger, I can see that the error is appropriately caught and that the ModelAndView object has the custom message inside of it. So, I have no clue why an exception is still being thrown after it has been caught and dealt with. Does anyone have insight into why this is happening?
On the #Transactional annotation, you can state whether or not to roll back your transaction due to a given exception using the noRollbackForClassName attribute. You can do it similar to this.
#Service
#Transactional(noRollbackForClassName = "java.lang.Exception")
public class YourClass {
...
}
However, note that just saying noRollbackForClassName = "java.lang.Exception" would mean it will not rollback for any Exception (or its subclasses), hence its not a good practice.
What you should do is, figure out what exception is actually thrown first (may be by printing out the e.getClass().getName()), then set that class name as the noRollbackForClassName value.
Reason wise, this is happening because if some exception is thrown while attempting to delete(), the current transaction is automatically marked as roll back only, and if it is attempted to be committed, the exception you see will be thrown. The way to get passed this is to explicitly state that this certain exception should not cause a roll back.
The issue is because once an exception is thrown, Spring internally marks the tx as rollback-only. This is completely separate from Java exception handling. You have several options:
Make sure your expected exception does not throw exceptions which extend RuntimeException; Spring only rolls back tx's when its a type RuntimeException (see this page, section 10.5.3). HibernateException extends RuntimeException, so that's why you're getting the rollback marker.
Run each tx in its own transaction by moving the transactional method to its own class and annotating it using #Transactional(propagation=Propagation.REQUIRES_NEW). Then each call will run in its own tx and will not affect the overall tx.
Use the noRollbackForClassName style venushka mentioned. But use with caution, for the reason mentioned.
The Exception is being thrown in Bar#delete and is caught in Foo#delete. There is a #Transactional annotation on Bar#delete which is crossed before the exception is caught. This inner transaction is participating in the outer transaction and so the entire transaction is marked for rollback.
To avoid this you could remove the #Transactional annotation for Bar#delete. This method is already called within the scope of the other transaction.
Add property "globalRollbackOnParticipationFailure" to the hibernateTransactionManager bean definition as follows.
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hibernateSessionFactory" />
**<property name="globalRollbackOnParticipationFailure" value="false" />**
</bean>

Resources