Анти-паттерн сессия-на-операцию

113
31 октября 2021, 00:20

В одной статье по Hibernate прочитал, что если вы хотите использовать многопоточность, то создавайте новую сессию для каждой CRUD-операции. То есть вот как выглядит, например, операция сохранения в моем DAO-классе:

public void save(User user) {
    try (Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession()) {
        Transaction tx = session.beginTransaction();
        session.save(user);
        tx.commit();
    }
}

Но вот незадача. Сегодня наткнулся на еще одну статью, в которой говорят, что описанное выше действие является анти-паттерном. Дословно:

Это анти-паттерн про открытие и закрытие объекта Session на каждую операцию к БД в одном потоке. Это также анти-паттерн в терминах транзакций БД. Группируйте ваши вызовы в одну запланированную последовательность. Также, не делайте авто-коммит транзакции на каждое SQL-выражение. Hibernate выключает, или ожидает, что сервер приложений немедленно выключит режим авто-коммита.

Заместо этого в этой статье предложен Паттерн сессия-на-запрос, где его описывают как:

Наиболее распространенный паттерн транзакций. Термин “запрос” здесь следует понимать в контексте системы, реагирующей на серии запросов от пользователя/клиента. Веб-приложения является основным примером таких систем, но, конечно, не только они одни. На этапе начала обработки запроса, приложение открывает объект Session, инициирует транзакцию, проводит всю сопутствующую работу с данными, завершает транзакцию и закрывает Session. Суть паттерна – это отношение один-к-одному между транзакцией и сессией.

После этого у меня возник когнитивный диссонанс. Во-первых потому, что оказывается согласно этой статье, раньше я делал не так, создавая сессию для каждой операции, а во-вторых потому, что я не понял, что предполагает второй паттерн. Объясните, что из этого правда, а что ложь? Что использовать, а про что забыть?

Answer 1

Если обратится к документации Hibernate - то по ней сессия-на-операцию официально обьявлена антипаттерном.

Что касается сессии, то сам ее смысл предполагает что вы можете и по возможности должны объединять связанные по смыслу операции в рамках одной или нескольких транзакций в одну сессию. В тоже время ничего плохого нет в том что ваша сессия может содержать лишь одну операцию, если кроме этой операции ничего делать не надо.

Чтобы понять разницу между первым и вторым приведу пример: представьте что существует еще один класс связанный с User, например UserHistory (пусть он будет хранить историю изменения имени пользователя). Мы предполагаем по смыслу что изменяя name в User должен обязательно обновляться и класс UserHistory.

Теперь представим что мы сделали все через operation-per-session. В этом случае у нас будет две разные сессии и две транзакции которые обновят User и UserHistory независимо друг от друга. Проблема возникнет тогда когда одна из транзакций не дойдет (например из за внезапных потерь на сети) что неизбежно приведет к нарушению целостности данных в БД.

И да, при работе с БД не через пулы - крайне ресурсоемко создавать и закрывать соединения с БД при каждом действии.

Answer 2

Действительно как пишется в документации к Hibernate:

The scope of a Hibernate org.hibernate.Session is flexible but you should never design your application to use a new Hibernate org.hibernate.Session for every database operation. Even though it is used in the following examples, consider session-per-operation an anti-pattern

Обратите внимание, что говорится о новой сессии, по сути предлагается использовать текущую сессию, а именно:

Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
//blah-blah
session.getTransaction().commit();

То есть если нет текущей сессии, то создаем, если есть берем текущую.

Создание сессии при каждой операции является антипаттерном, но не потому, что типа в другой сессии будет произведено обновление или там не дойдет до конца транзакция (менеджер транзакций СУБД вполне умеет с такими вещами работать), а из-за стоимости создания сессии - по сути это новый коннект к БД - крайне дорогая и ресурсоемкая операция.

READ ALSO
ошибка при передаче данных между серверами

ошибка при передаче данных между серверами

В универе задали задание сделать сайт с усложненной архитектурой, чтобы был сервер на PHP, который от клиента принимал запросы, потом отправлял...

169
Обработка нажатия на оповещение

Обработка нажатия на оповещение

Нужно чтобы при нажатии на оповещение с определенным текстом открывалась определенная вкладка viewPager, но как это реализовать?

101
Чтения файла построчно

Чтения файла построчно

Написал в пятницу работало сегодня не работает что я не так делаю ?

75
Как заполнить пустое поле CSS [закрыт]

Как заполнить пустое поле CSS [закрыт]

Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском

94