javamodel-view-controllertransactionstransactionmanager

How to implement Transaction Manager for multiple Dao


I am writing a Java application following the MVC pattern, abstract enough to allow the backend to connect to an Sql and a NoSql database. I have 4 model classes and 4 DAO interfaces. Each DAO is implemented in two ways, one for sql and one for mongodb. I want to add a Transaction Manager to execute database operations in atomic transactions: the problem is that these operations involve a variable number of DAOs and I don't know how to implement the Manager with more than one Dao. I'm not using Spring and I'm not willing to add it.

I've had the following ideas but each one has something I'm not sure about:

What I would do if I had only one Dao

public interface TransactionManager {
    <T> T executeTransaction(TransactionCode<T> code);
}

@FunctionalInterface
public interface TransactionCode<T> extends Function<MyDAO, T> {
}

public class ServiceLayer {

    private TransactionManager transactionManager;

    public ServiceLayer(TransactionManager manager) {
        transactionManager = manager;
    }

    public void doSomethingInTransaction() {
        transactionManager.executeTransaction((myDao) -> {
            myDao.doSomething();
            return null;
        });
    }
}

public class TransactionManagerMysql implements TransactionManager {

    private EntityManager entityManager;

    public TransactionManagerMysql(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public <T> T executeTransaction(TransactionCode<T> code) {
        try {
            entityManager.getTransaction().begin();
            T result = code.apply(new MyDaoSQL(entityManager));
            entityManager.getTransaction().commit();
            return result;
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        }
    }

}


Solution

  • I was sure I had to extend Function to a custom N-Function in order to be able to pass multiple input parameters (because this is how my teacher explained it). Well, I found out that I don't need to use Function, not even in the single-input scenario. Here the only purpose of extending Function is to invoke its method apply() from ServiceLayer. Instead of generalising to an N-Function I can just define a method with an adeguate number of inputs. To answer my question is enough to rewrite TransactionCode as follows:

    @FunctionalInterface
    public interface TransactionCode<T> {
    
        T apply(MyDaoA daoA, MyDaoB daoB, MyDaoC daoC, MyDaoD daoD);
    
    }