Use of BeforeDetachEntityListener - after commit update

I want to use a methodology by which an update to an Entity will trigger some subsequent action after commit, say update some other Entity in the database after commit action. For example, after the invoice transaction is committed, i want to update the customer’s outstanding amount in another table that can be done at background after commit action without holding the client until it is done. Will BeforeDetachEntityListener work here?

All listeners are executed synchronously, so you need to do your business logic in a separate thread. In the following example a Bar instance is created asynchronously each time a Foo instance is created or updated:


@Component("sample_FooEntityListener")
public class FooEntityListener implements AfterInsertEntityListener<Foo>, AfterUpdateEntityListener<Foo> {

    private ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Inject
    private Persistence persistence;

    @Inject
    private Metadata metadata;

    @Override
    public void onAfterInsert(Foo entity, Connection connection) {
        updateBarAsync(entity, true);
    }

    @Override
    public void onAfterUpdate(Foo entity, Connection connection) {
        updateBarAsync(entity, false);
    }

    private void updateBarAsync(Foo foo, boolean creating) {
        // submit a task for asynchronous execution
        // use SecurityContextAwareRunnable for passing security context to the new thread
        executorService.submit(new SecurityContextAwareRunnable(() -> {
            // some artificial delay
            sleep();
            // your business logic in a new transaction
            try (Transaction tx = persistence.createTransaction()) {
                Bar bar = metadata.create(Bar.class);
                bar.setName((creating ? "Created " : "Updated ") + foo.getName());
                persistence.getEntityManager().persist(bar);
                tx.commit();
            }
        }));
    }

    private void sleep() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

See the sample project attached.

async.zip (29.4K)

1 Like

Thank you very much Konstantin. A quick follow up question, Do you suggest we can also use service to run business logic for the new transaction?

It’s just a question of organization of your code, so it’s up to you. Usually it makes sense to encapsulate business logic in managed beans. If it is also invoked from the client tier, make it a service.

Hi Konatantin
This is working fine both inside the listener and in the service called from listener. A followup question, what will happen to this secondary operation if the commit is failed at the secondary update?

If you do something in a separate transaction and it fails, it doesn’t affect other transactions.

Thanks. How about the example you shared? i.e. when we use the executorService within the Entity listener I understand it will recall the whole transaction but when the transaction is not failed, will my resources be free and executor service run in the background after commit action without holding the client until it is done?

ExecutorService instance here is stored in the object field, and the object here is a singleton managed bean, so it serves all transactions. A new Runnable is submitted for each transaction to the same executor.

Does it mean that if that new runnable is failed, the whole transaction will be rolled back?

Only the transaction created in that Runnable. It won’t affect the transaction in the entity listener.