Elegant way of handling a large set of db operations and rollback in case of errors

Hi Cuba team,

I need to do a set of operations on entities and commit only if everything executed without errors.
Of course DataManager is not suited for this because I cannot control when the transaction is created, when my changes are flushed in the database and when it is closed.
I turned my eyes to Persistence and EntityManager beans.

I have some pieces of code in various services which work with my entities. These methods are called both individually from various screens as well as from one method that aggregates all of them together.

So I wanted a way to treat both cases in a single method: sometimes I execute it without having an active transaction (case in which I need to create one on the spot, run my scripts, commit and close it) and sometimes I have an active transaction (case in which I need to run my script that modifies entities, these entities are attached to the current active transaction, then exit the scope without closing the active transaction or committing, these steps will be done in the context that created the transaction).

I implemented it using lambda expressions as follows.

public class TransactionHandler {

private static final Persistence persistence = AppBeans.get(Persistence.NAME);


public static void handleAndRun(LambdaCodeExecutor codeExecutor) {
    if (persistence.isInTransaction()) {
        codeExecutor.run();
    } else {
        try (Transaction transaction = persistence.createTransaction()) {
            codeExecutor.run();
            transaction.commit();
        }
    }
}

public static <T extends StandardEntity> T handleAndRun(LambdaCodeExecutorWithValueReturn<T> codeExecutor) {
    if (persistence.isInTransaction()) {
        return codeExecutor.run();
    } else {
        try (Transaction transaction = persistence.createTransaction()) {
            T result = codeExecutor.run();
            transaction.commit();
            return result;
        }
    }
}

public static <T extends StandardEntity> List<T> handleAndRun(LambdaCodeExecutorWithListReturn<T> codeExecutor) {
    if (persistence.isInTransaction()) {
        return codeExecutor.run();
    } else {
        try (Transaction transaction = persistence.createTransaction()) {
            List<T> result = codeExecutor.run();
            transaction.commit();
            return result;
        }
    }
}

}

These are some examples of how I use it:

TransactionHandler.handleAndRun(
        () -> persistence.getEntityManager().remove(managedFile.getFile())
);    

List<Certificate> certificates = TransactionHandler.handleAndRun(
        () -> persistence.getEntityManager().createQuery(
               "select c from fb$Certificate c " +
              " where c.bo.id = :boId", Certificate.class
             ).setViewName(finalView)
                  .setParameter("boId", bo.getId())
                  .getResultList()
);

Is there a way to accomplish the same results in a more elegant way? It would be awesome not to go through lambda expressions every time I need to do some business logic.

Thank you in advance,
Andrei

Hi Andrei

Just use getTransaction() instead of createTransaction(), and your code will join an existing transaction or create a new one if no transaction currently exists. See Javadocs and documentation for details.
The same effect is achieved with the @Transactional annotation on a bean method.

See also TransactionalDataManager, it can be convenient than EntityManager.