V
V
Vasily Rybin2018-07-07 15:30:24
Java
Vasily Rybin, 2018-07-07 15:30:24

Organization of transaction processing in the Service Layer, does such an approach have the right to life?

Good afternoon to all residents! In the process of learning Java EE in courses, I encountered such a problem: it is
necessary to do transaction processing in the service layer in order to make the operation atomic for the backend user. Used by hibernate. spring for transactions is not yet allowed, and I'm looking at . I read, I'm looking for ways who implemented how and which implementation can be adequate .. there is one thought, but I want to know the advice, can someone tell me if it's right to do this.
Application - Administration of orders in a car service
I have 3 DAOs (they only contain crud, there are no transactions, work is being done through Session). There is a service, WorkShopService, which describes the logic of working with data.
Here, for example, is a code fragment from the service (I won’t give the whole one - it’s quite long)

public class WorkShopServiceImpl implements WorkShopService {
    @ConfigProperty(propName = "orderDAO", type = OrderDAO.class)
    private OrderDAO orderDAO;

    // дао для рабочих-мастеров
    @ConfigProperty(propName = "masterDAO", type = MasterDAO.class)
    private MasterDAO masterDAO; 

    // дао для рабочий мест-гаражей (у меня может быть их несколько)
    @ConfigProperty(propName = "workplaceDAO", type = WorkPlaceDAO.class)
    private WorkPlaceDAO workplaceDAO;

    // методы.... 
    public List<WorkPlace> countFreeWorkPlace(Date date) {
        // метод выводит список свободных рабочих мест на указанную дату
        // исходя из количества мастеров, заказов
    }

    // методы.... 
}


I need to make a method so that it works within a single transaction (maybe an unsuccessful example for a method, because it does not update data).
I found on a foreign site a description of working with transactions through a proxy service. That is, we are wrapping this WorkShopServiceImpl in another service. we duplicate the methods and in it, in each duplicated method, we first start the transaction, then we call the method from WorkShopServiceImpl , and after commit or rollback.
Code example
public class TransactionalWorkShopService implements WorkShopService {

  @ConfigProperty(propName = "service", type = WorkShopService.class)
  private WorkShopService service;

  @ConfigProperty(propName = "sessionProvider", type = SessionProvider.class)
  private SessionProvider sessionProvider;

  private Transaction tx;

  public TransactionalWorkShopService() { 

  }

  // какие то методы ...
  //...

   public List<WorkPlace> freeWorkPlace(Date date) {
   	try {
   		tx = sessionProvider.getSession().startTransaction();
   		List<WorkPlace> res = service.freeWorkPlace(date);
   		tx.commit();
   		return res;
   	} catch(HibernateException e) {
   		handleException(e);
   		throw e;
   	}
   }

   // какие то методы ...
  //...


   private void handleException(Exception e) {
   	try {
   		if(tx != null) tx.rollback();
   	} catch(Exception e1) {
   		log.error("Cannot rollback transaction",e1);
   	}
   	log.error(e); 
   }
}

As a result, we get another service layer that manages purely transactions, while the business layer knows nothing about them and is purely coordinated on applied tasks. And we "slip" the implementation of TransactionalWorkShopService to the backenda user, and in this case, all operations for him are atomic.
Does this approach have the right to life if you do not resort to using declarative transaction management tools? Or it’s better not to do this so as not to create a new layer, but in this case I still don’t know how else to make the method atomic .. load the layer, which is responsible only for application tasks, with information about working with transactions .. is it right to do so ?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Kolesnikov, 2018-07-15
@VasilyRybin

Of course not. You will have to copy-paste these decorators for each service you want to wrap in a transaction. You can do something like this:

public interface TransactionalOperation {

    void doOperation();

}

public class TransactionalHandler {

    @Autowired
    private SessionProvider sessionProvider;

    void doTransaction(TransactionalOperation transactionalOperation) {
        // Create transaction
        transactionalOperation.doOperation();
        // Commit and roolback handler
    }

}

public class SomeService {

    @Autowired
    private TransactionalHandler transactionalHandler;

    public void doMake() {
        transactionalHandler.doTransaction(() -> {
            // Do something in transaction
        });
    }

}

The TransactionalOperation interface can be replaced with any Java8 functional interface with other options as you wish and need. Thus, the TransactionalHandler class encapsulates the transaction processing logic and accepts a lambda with your business logic as input. This approach can be used in any services as needed without copy-paste of unnecessary code.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question