Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Events after Transaction Commit Spring JPA

I need to do some specific action after the successful transaction commit, which basically includes my analytical operations about the data;

I tried using the following code snippet

public class EntityEventHandlers {
    private static Logger logger = LoggerFactory.getLogger(EntityEventHandlers.class);

    @Autowired
    private AsyncEventBus async;

    @PostUpdate
    public void postUpdate(Order order) {
        AutowireHelper.autowire(this, this.async);
        logger.debug("post update called " + order.getId());
        async.post(new ESCreateOrderEvent(order));
    }

    @PostPersist
    public void postPersist(Order order) {
        AutowireHelper.autowire(this, this.async);
        logger.debug("post insert called " + order.getId());
        async.post(new ESCreateOrderEvent(order));
    }
}

But found that by when my the order is not persisted.

Can somebody tell me a better solution where I can trigger some action after transaction commit is successful.

I heard(tried using) about @TransactionEventListener, but didnt see anything triggering.

Updated the source

@Component
public class TXEventHandler {
    @TransactionalEventListener
    public void doAfterCommit(ApplicationEvent event){
        //process here
    }
}

About the application Its a Spring MVC based on 4.2.0 and uses Hibernate and MySql as the db.

Right now I have solved the problem by putting the event into a delay queue and the delay is sufficient to happen the db commit. But I know its not a good solution. So let me know if someone have faced this issue and able to fix it.

Thanks In Advance.

like image 659
Ysak Avatar asked Dec 25 '22 14:12

Ysak


1 Answers

Assuming, as your comment mentions, that you are using an entity manager to persist/merge your entity object, you probably have a Bean that wires it in. If so, then in order to take advantage @TransactionalEventListener, you want to wire in ApplicationEventPublisher bean. You class might look like:

@Component
public class OrderManager {
    @PersistenceContext
    protected EntityManager entityManager;

    // You need this Spring bean
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    // ...
    @Transactional
    public void saveOrder(Order newOrder) {
        entityManager.persist(newOrder);
        applicationEventPublisher.publishEvent(new OrderEvent());
    }

    @TransactionalEventListener
    public void doAfterCommit(OrderEvent event){
        //process here
    }

    // inner class for brevity, you may not want to do this in practice
    public static class OrderEvent {
    }   
}

This code (although horribly put together in a single class..) is just to illustrate this point: if you want @TransactionalEventListener to trigger, then you need to (at least):

  1. Define it in a Spring managed bean
  2. Wire in an "ApplicationEventPublisher" somewhere where your @Transactional lives (doesn't have to be the same bean at Step 1)
  3. Call "applicationEventPublisher.publishEvent()" inside @Transactional method

The default behaviour will trigger the TransactionalEventListener after the commit is complete & entity manager is flushed, but still within the bounds of the transaction. You can change this within the annotation with the 'phase' param, but this should be enough to address your issue.

ps. you can glean most of this from https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2 as JB Nizet says

like image 85
paulb Avatar answered Dec 28 '22 08:12

paulb