Updating an existing piece of code as shown below:
@Override
@Transactional
public void update(SomeObject someObj) throws BusinessServiceException {
...
someObjectRepository1.update(someObj);
someObjectRepository2.create(someObj);
//New code below:
someObjectRelatedService.doStuff(someObj); // <--new code
//Throws error, object does not exist in someObjectRepository2
}
The issue here is that someObjectRelatedService.doStuff needs the latest and most updated version of someObjectRepository2 - but the transactional annotation prevents that from happening, it updates the repository AFTER the method has fully ran. I was able to verify by using a debugger and running a database call in between.
What would be the best way to approach this situation? Is it solely to extract the someObjectRelatedService call outside the transactional method?
In essence, you need to move the boundary of your transaction. While manually flushing the transaction is an option, I would not recommend to do so.
The easiest fix, if the semantics of SomeObjectRepository2#create(...)
permit it, would be to annotate this method with @Transactional(isolation = Propagation.REQUIRES_NEW)
.
If this should not be possible, I propose the following minor redesign. First, we create a new class with one method, which should execute the code related to the data that is needed:
@Component
public class NewClass {
final SomeObjectRepository2 someObjectRepository2;
@Autowired
public NewClass(final SomeObjectRepository2 someObjectRepository2) {
this.someObjectRepository2 = someObjectRepository2;
}
@Transactional(isolation = Propagation.REQUIRES_NEW) // enforces new transaction
public WhateverNeedsToBereturned newMethod(WhateverType someObject) {
return someObjectRepository2.create(someObj);
}
}
And then we rewrite the existing code to use the new class:
@Override
@Transactional
public void update(SomeObject someObj) throws BusinessServiceException {
...
someObjectRepository1.update(someObj);
newClassInstance.newMethod(someObj);
//New code below:
someObjectRelatedService.doStuff(someObj);
//Throws error, object does not exist in someObjectRepository2
}
Note that you have to create a new class in order for this to work. Method calls through the this
-reference are not proxied and thus the @Transactional
annotation does not take effect.
Note further that newClassInstance
must be injected through the DI container for the same reason.