0

I'm trying to chain Spring transaction events, but the second @TransactionalEventListener is not triggered when an event is published from within another AFTER_COMMIT listener. Here's a minimal example:

@Service
public class EventService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @Transactional(rollbackFor = Exception.class)
    public void startProcess() {
        // This is called within a transaction
        eventPublisher.publishEvent(new FirstEvent());
    }
    
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleFirstEvent(FirstEvent event) {
        System.out.println("FirstEvent handled successfully");
        
        // Trying to publish another event here
        eventPublisher.publishEvent(new SecondEvent());
    }
    
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleSecondEvent(SecondEvent event) {
        // This is NEVER called!
        System.out.println("SecondEvent handled");
    }
}

What happens:

  • startProcess() is called within a transaction
  • FirstEvent is published
  • After transaction commits, handleFirstEvent() is triggered ✅
  • SecondEvent is published from within handleFirstEvent()
  • handleSecondEvent() is NEVER triggered ❌

What I've tried:

Annotation on handleFirstEvent() handleSecondEvent() triggered?
No annotation ❌ NO
@Transactional ❌ NO
@Transactional(propagation = Propagation.REQUIRES_NEW) ✅ YES
@Async("asyncExecutor") ✅ YES

Questions:

Why doesn't @Transactional on handleFirstEvent() create a proper transaction synchronization context when it's executed in the AFTER_COMMIT phase, causing the second @TransactionalEventListener to miss the event?

My logical expectation:

  1. startProcess() runs in transaction T1
  2. FirstEvent is published and bound to T1
  3. T1 commits → triggers handleFirstEvent() in AFTER_COMMIT phase
  4. handleFirstEvent() has @Transactional, and since T1 is already committed, Spring Framework should create a completely NEW transaction T2
  5. SecondEvent is published within T2
  6. T2 commits → should trigger handleSecondEvent() in T2's AFTER_COMMIT phase
  7. handleSecondEvent() should execute

But in reality: Step 7 never happens. handleSecondEvent() is never called.

What's even more confusing:

When I change to @Transactional(propagation = Propagation.REQUIRES_NEW), it suddenly works! But why?

  • Both REQUIRED (default) and REQUIRES_NEW should create a new transaction when no transaction exists
  • After T1 commits, there's no active transaction, so REQUIRED should behave like REQUIRES_NEW

1 Answer 1

0

TLDR;

You are creating in actual only single transaction but expecting to run both callbacks that will run on different transactions altogether

If you look closely

  1. For first event, @Transactional annotation is present on method. It creates a spring transaction for you.

  2. Then TransactionalEventListener is called and it correctly publishes first event.

  3. This is the step where you are mixing general Spring event publishing vs on transactional callbacks.

    Referring to line

     eventPublisher.publishEvent(new SecondEvent());
    

    Here, you are publishing event yourself not via transaction. To handle this you will have to have a ApplicationListener for listening to this event.

  4. See example below:

@Component
public class CustomSpringEventListener implements ApplicationListener<SecondEvent> {
    @Override
    public void onApplicationEvent(SecondEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

For more on Spring Events refer: https://www.baeldung.com/spring-events

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.