@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, и в таком случае все операции для него являются атомарными..
Такой подход имеет право на жизнь, если не прибегать к использованию средств декларативного управления транзакциями? Или так лучше не делать дабы не создавать новый слой, но в таком случае я пока не знаю, как можно еще сделать метод атомарным.. нагружать слой, который ответственнен только за прикладные задачи, информацией о работе с транзакциями.. это правильно ли будет так?
  • Вопрос задан
  • 369 просмотров
Решения вопроса 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 инкапсулирует логику обработки транзакций и принимает на вход лямбду с вашей бизнес логикой. Такой подход можно использовать в любых сервисах по мере надобности без копипаста ненужного кода.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы
Bell Integrator Ульяновск
До 400 000 ₽
Bell Integrator Хабаровск
До 400 000 ₽
Bell Integrator Ижевск
До 400 000 ₽
25 апр. 2024, в 15:31
70000 руб./за проект
25 апр. 2024, в 15:26
15000 руб./за проект
25 апр. 2024, в 15:13
3000 руб./за проект