Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@TransactionalEventListener doesn't works where as @EventListener works like charms for the same context

I'm working in an event-driven design of spring boot application.

The code consists of following files:

  1. Spring boot: ApplicationEvent File i.e MyBusinessEvent.{java

    @Data
    @AllArgsConstructor
    public class MyBusinessEvent {
    
       private String data;
    
    }
    
  2. Event Publisher File: MyBusinessService.java

    @Slf4j
    @Service
    public class MyBusinessService {
    
        private final ApplicationEventPublisher applicationEventPublisher;
    
        @Autowired
        public MyBusinessService(
                ApplicationEventPublisher applicationEventPublisher) {
    
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        @Override
        public void save() {
            String data = "Testing event data";
            MyBusinessEvent event = new MyBusinessEvent(data);
            applicationEventPublisher.publishEvent(event);
        }
    }
    
  3. EventListener : MyBusinessEventListener.java

    @Slf4j
    @Component
    public class MyBusinessEventListener {
    
        @EventListener
        public void handleEvent(MyBusinessEvent myBusinessEvent) {
            log.info("[MyBusinessEventListener] New event received with following data: {}", myBusinessEvent);
        }
    }
    

The point is when Listener file consists @EventListener then the app works as expected but when I do @TransactionEventListener. For example:

  1. TransactionalEventListener : MyBusinessEventListener.java

    @Slf4j
    @Component
    public class MyBusinessEventListener {
    
        @TransactionalEventListener
        public void handleEvent(MyBusinessEvent myBusinessEvent) {
            log.info("[MyBusinessEventListener] New event received with following data: {}", myBusinessEvent);
        }
    }
    

with @TransactionalEventListener, it doesn't work at all.

The application doesn't through any exception (not even at runtime) but there is no logging as expected.

Is any configuration missing?

like image 742
virsha Avatar asked Jan 28 '23 20:01

virsha


1 Answers

Add @Transactional annotation to MyBusinessService.save should fix this issue.

As per java documentation, @TransactionalEventListener work within @Transactional boundary.

Here what documentation says,

If the event is not published within the boundaries of a managed transaction, the
 * event is discarded unless the {@link #fallbackExecution} flag is explicitly set.

If you don't want your service method in transaction boundary then use @TransactionalEventListener(fallbackExecution = true)

EDIT: There are three possible solutions to the above problem.

i. Mark class as @Transactional to be considered as transactional

@Transactional
@Service
public class MyBusinessService {

    private final ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    public MyBusinessService(
            ApplicationEventPublisher applicationEventPublisher) {

        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public void save() {
        String data = "Testing event data";
        MyBusinessEvent event = new MyBusinessEvent(data);
        applicationEventPublisher.publishEvent(event);
    }
 }

ii. Make the specific method of service as @Transactional

@Override
@Transactional
public void save() {
    String data = "Testing event data";
    MyBusinessEvent event = new MyBusinessEvent(data);
    applicationEventPublisher.publishEvent(event);
}

iii. If you don't want your service method in transaction boundary then use

@Slf4j
@Component
public class MyBusinessEventListener {

    @TransactionalEventListener(fallbackExecution = true)
    public void handleEvent(MyBusinessEvent myBusinessEvent) {
        log.info("[MyBusinessEventListener] New event received with following data: {}", myBusinessEvent);
    }
}
like image 85
Shaunak Patel Avatar answered Jan 31 '23 09:01

Shaunak Patel