@VasilyRybin
Java Developer

Организация обработки транзакций в Service Layer, имеет ли право на жизнь подобный подход?

Добрый день всем жителям! В процессе обучения Java EE на курсах, столкнулся с такой проблемой:
необходимо сделать обработку транзакций в service layer чтоб сделать операцию атомарной для пользователя backenda. Используется hibernate. spring для транзакций использовать пока запрещено, и я смотрю . читаю, ищу способы кто как реализовывал и какая реализация может быть адекватной.. есть одна мысль но я хочу узнать совета, может кто подскажет - правильно ли так делать.
Приложение - Администрирование заказов в автосервисе
У меня есть 3 DAO (в них только crud, транзакций нет, идет работа через Session). Есть сервис, WorkShopService, в котором описана логика работы с данными.
Вот, к примеру, фрагмент кода из сервиса (весь приводить не буду - он длинный довольно)
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) {
        // метод выводит список свободных рабочих мест на указанную дату
        // исходя из количества мастеров, заказов
    }

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


Мне нужно сделать метод , чтоб он работал в рамках одной транзакции (может неудачный пример для метода, потому что в нем не происходит обновление данных).
Нашел на зарубежном сайте описание работы с транзакциями через прокси-сервис. То есть вот этот мой WorkShopServiceImpl мы оборачиваем в другой сервис. дублируем методы и в нем в каждом продублированном методе сначала начинаем транзакцию, потом вызываем метод из WorkShopServiceImpl , и после commit или rollback.
Пример кода
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); 
	 }
}

В итоге у нас получается еще один сервис-слой, который управляет чисто транзакциями, а бизнес-слой о них ничего не знает и чисто скоординирован на прикладных задачах. А пользователю backenda мы "подсовываем" реализацию TransactionalWorkShopService, и в таком случае все операции для него являются атомарными..
Такой подход имеет право на жизнь, если не прибегать к использованию средств декларативного управления транзакциями? Или так лучше не делать дабы не создавать новый слой, но в таком случае я пока не знаю, как можно еще сделать метод атомарным.. нагружать слой, который ответственнен только за прикладные задачи, информацией о работе с транзакциями.. это правильно ли будет так?
  • Вопрос задан
  • 390 просмотров
Решения вопроса 1
@kejinzo
Java Developer
Конечно нет. Вам придется копипастить эти декораторы для каждого сервиса который вы хотите обернуть в транзакцию. Можно сделать примерно так:

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
        });
    }

}


Интерфейс TransactionalOperation можно заменить на любой функциональный интерфейс Java8 с другими параметрами по своему желанию и потребностям. Таким образом класс TransactionalHandler инкапсулирует логику обработки транзакций и принимает на вход лямбду с вашей бизнес логикой. Такой подход можно использовать в любых сервисах по мере надобности без копипаста ненужного кода.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы