I just started to migrate an old Java 7 project to Java 8 and Spring 5 and started (ab)using lambda expressions, converting some old-styled code. The following is described for TransactionOperations but applies perfectly to HibernateOperations, so I won't open clone issues.

One of the patterns I am having a hard time with is the TransactionOperations.execute(). Often I have to programmatically instantiate a transaction and run a void method within this transaction. I am having this requirement now that I am updating my unit tests, because production code is wrapped by @Transactional.

So, let me say that simple. If I have to run a single void method in a transaction I have to

    new TransactionTemplate(transactionManager).execute(ignored -> {
        messageManager.populatePlaceholders(mockModuleCatalog());
        return null;
    });

ignored is the TransactionStatus object. Not everyone needs it but I can live with that. The above works but adds overhead because of the return value. I would prefer something like

    new TransactionTemplate(transactionManager).execute(()->messageManager.populatePlaceholders(mockModuleCatalog()));

Or

    new TransactionTemplate(transactionManager).execute(ignored ->messageManager.populatePlaceholders(mockModuleCatalog()));

Or, of course, some other developer may want to use the TransactionStatus object

    new TransactionTemplate(transactionManager).execute(status->messageManager.populatePlaceholders(mockModuleCatalog(),status));

Suggestion is: add overloads to TransactionOperations.execute() to accept lambda expressions that do not return values and/or do not require transaction status. Ideally, Runnable and Consumer<TransactionStatus> are perfect super-interfaces for the new overloads.

Currently Spring Framework provides TransactionCallbackWithoutResult that is perfect for my objective. The problem is that it doesn't fit with lambda expressions. After all, that class was made in 2003 when Java 8 did not probably exist yet.

I would love to open a sample PR with default method implementations if someone is interested.

Comment From: timomeinen

You can create helper functions. In this example with the usage of a shared EntityManager:

@PersistenceContext
private EntityManager entityManager;

void transactional(TransactionalVoidFunction function) {
    transactionTemplate.execute((TransactionCallback<Void>) status -> {
        function.accept(entityManager);
        return null;
    });
}

<T> T transactional(TransactionalFunction<T> function) {
    AtomicReference<T> result = new AtomicReference<>();

    transactionTemplate.execute(status -> {
        result.set(function.apply(entityManager));
        return null;
    });

    return result.get();
}

Usage:

    transactional(entityManager -> {
        entityManager.persist(new Entity());
    });

    Entity entity = transactional(entityManager -> {
        return entityManager.find(Entity.class, 1L);
    });

Comment From: sbrannen

This support was already added to TransactionOperations in Spring Framework 5.2 in the form of executeWithoutResult(Consumer<TransactionStatus>).

However, I'm leaving this issue open in order to consider adding similar support in HibernateOperations.

Comment From: snicoll

However, I'm leaving this issue open in order to consider adding similar support in HibernateOperations.

This hasn't really raised much interest so I am going to close this one.