Spring - How to get reference to TransactionStatus object in a Transactional method - java

When using #Transactional in a service layer, is there any option to get a reference to the TransactionStatus instance created by the PlatformTransactionManager?
For instance, in the following code :
#Transactional
public void updateCustomer(...) {
// do some business stuff
// can we get here a reference to the TransactionStatus instance ?
}

TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
The currentTransactionStatus method returns the transaction status of the current method invocation.
If you are interested in the result of a transaction, you could consider the TransactionSynchronizationAdapter which provides a convenient afterCompletion(int status) callback:
class AfterCompletionTransactionHandler
extends TransactionSynchronizationAdapter {
public #Override void afterCompletion(int status) { ... }
}

Related

Incorporating Guice and AOP

I'm building a package that is trying to intercept a function's return value based on a flag. My design involves some AOP. The idea is that a class FirstIntercept intercepts a call firstCall and stores parameters in a Parameters object. Then later, a second class SecondIntercept intercepts another call secondCall and does some logic based on what is populated in Parameters:
// pseudoish code
public class FirstIntercept {
private Parameters param;
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// logic handling payload returned from firstCall()
// logic provides a Boolean flag
this.param = new Parameters(flag);
}
}
public class Parameters {
#Getter
private Boolean flag;
public Parameters(Boolean flag) {
this.flag = flag;
}
}
public class SecondIntercept {
private static Parameters params;
#Around("execution(* ...secondCall(..))")
public void handleSecondCallIntercept(ProceedingJoinPoint joinPoint) {
// want to do logic here based on what params contains
}
}
What I want to achieve is that the Parameters object is loaded once and for all when FirstIntercept.loadParam is invoked through AOP. I'm not too sure how I can go about with this persistence. I looked online and Google guice seems to be promising. I believe a first step would to use dependency injection on the Parameters, but I'm really not sure. Can someone help point me in the right direction?
edit:
So I tried this setup:
public class FirstIntercept implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("invoked!");
return invocation.proceed();
}
#AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
public void loadParam(Joinpoint joinPoint, Object payload) {
// do stuff
}
public String firstCall() {
return "hello";
}
}
public class InterceptionModule extends AbstractModule {
protected void configure() {
FirstIntercept first = new FirstIntercept();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class), first);
}
}
public class FirstIterceptTest {
#Test
public void dummy() {
Injector injector = Guice.createInjector(new InterceptionModule());
FirstIntercept intercept = injector.getInstance(FirstIntercept.class);
intercept.firstCall();
}
}
When I do .firstCall(), I can see the #AfterReturning running but the invoke is not being called.
If you expand upon the documentation for AOP https://github.com/google/guice/wiki/AOP you should get something close to:
public class FirstInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
// your logic based on result to set parameters.setFlag()
return result;
}
}
Then the second:
public class SecondInterceptor implements MethodInterceptor {
#Inject Parameters parameters; // Injected with singleton Parameter
public Object invoke(MethodInvocation invocation) throws Throwable {
boolean flag = parameters.getFlag();
// your logic here
return invocation.proceed(); // maybe maybe not?
}
}
Your parameters is the key, you'll need to ensure it's thread safe, which is another topic. But to inject these you need:
public class InterceptionModule extends AbstractModule {
protected void configure() {
// Ensure there is only ever one Parameter injected
bind(Parameter.class).in(Scopes.SINGLETON);
// Now inject and bind the first interceptor
FirstInterceptor firstInterceptor = new FirstInterceptor();
requestInjection(firstInterceptor );
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
firstInterceptor);
// Now inject and bind the second interceptor
SecondInterceptor SecondInterceptor = new SecondInterceptor ();
requestInjection(firstInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
SecondInterceptor);
}
}
Edit
Look at what you're doing.
You're telling Guice to wrap a method with #AfterReturn with the FirstInterceptor
Then you're calling interceptor.firstCall()
First call does not have #AfterReturn annotation, so why would it be matched against that configuration?
I'm guessing if you called:
intercept.loadParam();
you would see the invoke method. Also, this is great for a test, but in real life you want to have a Service level class have the #AfterReturn which is then Injected into another Api/Job/Etc that will call LoadParam.
edit
Oh no. Take a look at this line
bindInterceptor(Matchers.any(), // a class with this matcher
Matchers.annotatedWith(AfterReturning.class), // a method with this
firstInterceptor);
This means that the injector only fires on the loadParams. You need to annotate the method of the class youw ish to cause the interception with #AfterReturning. And you want the loadParams to be the invoke method.

Spring boot #Async annotation throw LazyInitializationException

I have an emailsenderservice to manage email notification asynchronously.
There are 2 async methods, one method works, but another one throws LazyInitializationException:
#Service
public class EmailSenderService {
// working
#Async
public void sendNewBidRequestEmail(BidRequest bidRequest) {
this.sendNewBidRequestEmailToSupplier(bidRequest);
}
#Transactional
public void sendNewBidRequestEmailToSupplier(BidRequest bidRequest) {
sendNewBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels());
}
#Transactional
public void sendNewBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList) {
for (Hotel hotel : hotelList) {
...
this.sender.send()
}
}
// not working, throw exception
#Async
public void sendCancelledBidRequestEmail(BidRequest bidRequest, String reason) {
this.sendCancelledBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels(), reason);
}
#Transactional
public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList, String reason) {
for (Hotel hotel : hotelList) { // throw exception here
...
this.sender.send();
}
}
I'm totally following this thread.
You can see both async methods have almost the same structure. Async method calls a transactional method. But the second one throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.corpobids.server.entity.BidRequest.hotels, could not initialize proxy - no Session.
I even imitate the first async method structure to modify the second one to:
#Async
public void sendCancelledBidRequestEmail(BidRequest bidRequest, String reason) {
this.sendCancelledBidRequestEmailToSupplier(bidRequest, reason);
}
#Transactional
public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, String reason) {
this.sendCancelledBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels(), reason);
}
#Transactional
public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList, String reason) {
for (Hotel hotel : hotelList) { // exception in this line
...
this.sender.send();
}
}
}
This time, it gives me java.lang.IllegalStateException: org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl#fce9b7b is closed.
I would like to know the missing point in my code, any help would be appreciated.
You have issues with JPA.
In JPA the relationships OneToMany has two behaviors LAZY and EAGER.
You can check a good explanation on: Difference between FetchType LAZY and EAGER in Java Persistence API?
I assume when you invoke your code in async mode the context of JPA is loose. So Hibernate can't fill the relationship executing a new query since the context is different. In order to fix your problem you have two options:
Configure the relationship with EAGER
Preload the relationship before invoke async method
Better techniques for lead with Lazy Loading:
https://www.thoughts-on-java.org/5-ways-to-initialize-lazy-relations-and-when-to-use-them/

Spring #Transactional annotation behaviour

I am building a workflow system, where a service layer - WorkflowServiceImpl, process a document and send notifications to users.
There is another service DocumentServiceImpl, which has a method post() method, which internally calls WorkflowServiceImpl.process() method.
#Service
public class WorkflowServiceImpl implements WorkflowService{
#Transactional(propagation=Propagation.REQUIRES_NEW, noRollbackFor=WorkflowException.class)
public void process(WorkflowDocument document) throws WorkflowException {
Boolean result = process(document);
if(!result){
throw new WorkflowException();
}
}
private Boolean process(WorkflowDocument document){
//some processing on document
updateDocument();
sendNotifications();
}
private void updateDocument(WorkflowDocument document){
//some update operation
}
private void sendNotifications(WorkflowDocument document){
//send notifications - insertion operation
}
}
#Service
public class DocumentServiceImpl implements DocumentService{
#Autowired private WorkflowService workflowService;
#Transactional
public void post(){
//some operations
workflowService.process(document);
//some other operations
}
}
As you can see, I have marked
DocumentServiceImpl.post() as #Transactional
WorkflowServiceImpl.process() as #Transactional(propagation=Propagation.REQUIRES_NEW, noRollbackFor=WorkflowException.class)
I am trying to achieve this :
1. WorkflowServiceImpl.process() method should commit always(update document and send notifications) - whether a WorkflowException is thrown or not
2. DocumentServiceImpl.post() method should rollback, when WorkflowException is thrown
When I tried using the above transaction configurations
1. When WorkflowException is not thrown, the code worked as expected - committed both WorkflowServiceImpl.process() and DocumentServiceImpl.post() methods
2. When WorkflowException is thrown, the request processing is not completed (I can see the request processing symbol in the browser)
I can't find what is wrong with the code. I am using spring version 3.1.4
You need to have a rollbackFor in the #Transactional annotation for WorkflowException and propagation as REQUIRES_NEW
#Transactional(rollbackFor = {WorkflowException.class}, propagation = Propagation.REQUIRES_NEW)
public void post(){
//some operations
workflowService.process(document);
//some other operations
}
This will make a new transaction to be started with post method of DocumentServiceImpl

Get spring transaction by it's ID and check status

Is there any way to get current transaction id, store/pass it, and check it's status in the another part of application?
For example:
#Service
public class Service {
...
#Transactional(rollbackFor = Exception.class)
public void performAction(Action action) {
// start action
String transactionId = ??? // getting current transaction id
messenger.send(transactionId); // sending transaction id to consumer
// continue action
} <- commit transaction
}
public class Consumer {
...
public void onRecieveMessage(String transactionId) {
TransactionStatus ts = ??? // getting transaction from pool by id
if (ts.isCompleted()) {
// some actions
} else {
// wating or Future<?> or something else...
}
}
}
(Actually, the problem is Consumer.onRecieveMessage executes earlier than action's transaction finishes, and data state is old)
I do not know any way of getting a specific transaction by its ID (hopefully someone does).
What I would do is to implement an instance of TransactionSynchronization that holds a kind of value you want to track:
public class YourTransactionTracker extends TransactionSynchronizationAdapter {
private Value value;
public YourTransactionTracker(Value value) {
this.value = value;
}
#Override
public void afterCompletion(int status) {
//transaction handling your 'value' has been completed with the status 'status'
//do whatever you want here
}
//you can also override other methods like beforeCompletion(), flush(), etc...
//have a look at TransactionSynchronizationAdapter
}
TransactionSynchronizationAdapter is just an implementation of TransactionSynchronization with empty methods.
Then you can hook a callback in the transaction with your TransactionSynchronization instance, that will be called in the different stages of the lifecycle of the transaction.
You can do this in Spring by using the TransactionSynchronizationManager:
TransactionSynchronizationManager.registerSynchronization(
new YourTransactionTracker(theValueYouAreTracking);

How to ensure #Entity exists in #Async methods?

I want to create an entity, and within the transaction trigger an #Async method to perform some changes on the same entity. The changes should also be persisted async.
Problem: as I have to fire the async method within the transaction, I could use the autogenerated #Id from the entity. BUT the async method then would have to first fetch the entity by that Id, and most often this does not exist yet.
Only if I put some Thread.sleep() as first statement inside the async method, it can mostly be ensured that the entity has been persisted by the outer transaction.
But that solution is not very nice. Question: how can I ensure inside the async method that it should wait for the entity to exist in DB?
#Service
public class OuterService {
#Service
private SyncService service;
#Transactional
public void process() {
service.mySyncMethod();
//etc
}
}
#Service
public class SyncService {
#Transactional
public void mySnycMethod() {
Entity entity = new MyEntity();
//fill entity
dao.save(entity);
asyncService.performLongRunningTask(entity.getId());
}
}
#Service
public class AsycnService {
#Async
#Transactional
public voi performLongRunningTask(Long id) {
//problem: is mostly == null because this is executed before the outer transaction completes
//only works if I put like Thread.sleep(5000) in between. but how can I really ensure the entity exists before executing this async lookup?
MyEntity entity = dao.findOne(id);
//perform long running task
//change some fields in entity accordingly
dao.save(entity);
}
}
You could register a hook on transaction commit using the TransactionSynchronizationManager.registerSynchronization() and implementing the afterCommit() method.
#Transactional
public void mySnycMethod() {
Entity entity = new MyEntity();
// fill entity
dao.save(entity);
// performLongRunningTask will start after the transaction has been
// commited
TransactionSynchronizationManager
.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
asyncService.performLongRunningTask(entity.getId());
}
});
}
But note what the Javadocs say about using the TransactionSynchronizationManager in your application:
To be used by resource management code but not by typical application
code

Categories

Resources