Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

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

#java #hibernate #java_ee #шаблоны_проектирования #java_8


В одной статье по 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.
Суть паттерна – это отношение один-к-одному между транзакцией и сессией. 


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


Ответы

Ответ 1



Если обратится к документации Hibernate - то по ней сессия-на-операцию официально обьявлена антипаттерном. Что касается сессии, то сам ее смысл предполагает что вы можете и по возможности должны объединять связанные по смыслу операции в рамках одной или нескольких транзакций в одну сессию. В тоже время ничего плохого нет в том что ваша сессия может содержать лишь одну операцию, если кроме этой операции ничего делать не надо. Чтобы понять разницу между первым и вторым приведу пример: представьте что существует еще один класс связанный с User, например UserHistory (пусть он будет хранить историю изменения имени пользователя). Мы предполагаем по смыслу что изменяя name в User должен обязательно обновляться и класс UserHistory. Теперь представим что мы сделали все через operation-per-session. В этом случае у нас будет две разные сессии и две транзакции которые обновят User и UserHistory независимо друг от друга. Проблема возникнет тогда когда одна из транзакций не дойдет (например из за внезапных потерь на сети) что неизбежно приведет к нарушению целостности данных в БД. И да, при работе с БД не через пулы - крайне ресурсоемко создавать и закрывать соединения с БД при каждом действии.

Ответ 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(); То есть если нет текущей сессии, то создаем, если есть берем текущую. Создание сессии при каждой операции является антипаттерном, но не потому, что типа в другой сессии будет произведено обновление или там не дойдет до конца транзакция (менеджер транзакций СУБД вполне умеет с такими вещами работать), а из-за стоимости создания сессии - по сути это новый коннект к БД - крайне дорогая и ресурсоемкая операция.

Комментариев нет:

Отправить комментарий