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.