Events after Transaction Commit Spring JPA
Asked Answered
G

2

6

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.

Gamp answered 20/9, 2015 at 9:20 Comment(8)
Spring 4.2 introduced transactional events to do exactly that. Read spring.io/blog/2015/02/11/…Pinnate
As I explained earlier, I tried to use it @TransactionEventListener...But unfortunately I couldnt wire it Up.Gamp
How about showing us the code of that attempt, then?Pinnate
I have updated the code that I added as a listener, My expectation is that since it is annotated as a Component, and have added @TransactionalEventlistner to the method, it should able to catch the Event. I dunno i have to register it manually to the spring transaction manager. I use the platformtransaction manager + with java entity manager for the hibernate operationsGamp
Where is the code which publishes the event? Why do you use ApplicationEvent as argument, and not your own event class, like OrderCreatedEvent in the example of the posted link? You might want to read the page I linked to.Pinnate
But at what point I should publish the event...in that case. I am using Transactional annotation from spring and PersistanceContext to get the access to EntityManager. If I can know when to publish i have already google event bus wired in my service, I can use that. But if I have to fire the event what spring transaction events will be doing.Gamp
If you want to do your analytical operations after a product has been saved and after an order has been updated, then you post an event from the method saving a product, and from the method updating an order. The listener is then called after the transaction saving the product or updating the order has been committed. It's up to you to publish the events you want, at the moment you want, and to do whatever you want when any of those events is published.Pinnate
My situation here is I published an even before my function returns(That function is annotated Transactional, so spring will automatically commit my changes).... But my event handler that thrown error saying the order with that Id doesnt exist in the database. The reason is because spring haven't committed the transaction yet. This is where I need the spring to let me know that the transaction is committed successfully.Gamp
I
13

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

Immunize answered 13/11, 2015 at 1:43 Comment(0)
A
-1

You can annotate your entity class with @EntityListeners(class=MyListenerClass.class).

In MyListenerClass you implement a method that is annotated with @PostUpdate and that gets an entity as a parameter:

@PostUpdate
public void onPostUpdate(Object entity) {
    /*your actions go here*/
}

Have a look here: https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/listeners.html

Achromic answered 31/5, 2022 at 8:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.