4
votes

I have a setup where an outer method calls an inner method. It is possible that this inner method will throw an exception that will cause it to rollback. I don't want this exception to affect the outer method. To achieve this, I used @Transactional(propagation=Propagation.REQUIRED_NEW) on the inner method.

Here is a simplified version of my code:

public class ServiceAImpl implements ServiceA{
    @Autowired
    private ServiceB serviceB;

    @Transactional(propagation=Propagation.REQUIRED)
    public void updateParent(Parent parent) {
        update(parent);
        serviceB.updateChild(parent);
    }
}

public class ServiceBImpl implements ServiceB {
    @Transactional(propagation=Propagation.REQUIRED_NEW)
    public void updateChild(Parent parent) {
        checkIfChildHasErrors(parent.getChild()); //throws RuntimeException if Child has errors
        update(parent.getChild());
    }
}

public class Parent {
    @Version
    private Integer version;
    private Child child;

    //insert getters and setters
}

public class Child {
    @Version
    private Integer version;

    //insert getters and setters
}

I am still new to Propagation but from my understanding, since the outer method (updateParent) has Propagation.REQUIRED and the inner method (updateChild) has Propagation.REQUIRED_NEW, they are now contained in their own separate transactions. If inner method encounters an exception, it will rollback but will not cause a rollback on the outer method.

When the outer method runs, it calls the inner method. The outer method is paused while the inner method is being ran. Once the inner method finishes, it is committed since it is a different transaction. The outer method unpauses. and it is also committed as another transaction.

The issue I am encountering is that the process of committing the outer method is triggering the optimistic locking for the Child class (probably because the value of the version field was changed after the inner method has ended and was committed). Since the outer method's instance of Child is already outdated, committing it triggers optimistic locking.

My question is: IS THERE A WAY TO PREVENT THE OUTER METHOD FROM TRIGGERING OPTIMISTIC LOCKING?

I am surprised that the outer method even tries to commit changes to the Child class. I assumed that since the inner method is contained in its own transaction, the outer method's transaction will no longer include the updateChild method.

I am using Spring 3.0.5 with Hibernate 3.6.10

1
One simple solution would be to catch OPTIMISTIC LOCK Exception of inner method from outer method and make it pass the exception.Esty
Hi Tanjim, it is the outer method that throws the optimistic lock exception. The inner method updates the DB record without exceptions. Also, i don't think its wise to catch and ignore the optimistic lock, I am screwed if a real optimistic lock exception is thrown via concurrent user updates.poach
Your propagation is correct. Using Propagation.REQUIRED on outer transaction and Propagation.REQUIRED_NEW on inner transaction will not affect outer transaction for any commit / rollback of inner transaction. Would u please determine that which of your rows are modifying during operation. I think u are working with same row in both transactions for which version field is getting updated and OptimisticLockException is thrown.Esty
Hi Tanjim, they are definitely updating the same row in the DBpoach
Then u must get the latest version of row after committing inner transaction and use that version in second transaction for update same row. Otherwise u will get OptimisticLockException.Esty

1 Answers

0
votes

Assuming you are using merge for update

For inner transaction

Entity entityUpdated = entityManager.merge(entity);

For outer transaction

if (entityUpdated != null){
    // if inner transaction rolledback entityUpdated will be null. Condition will save u from nullPointerException
    outerEntity.setVersion(entityUpdated.getVersion);
}

entityManager.merge(outerEntity);