Страницы

воскресенье, 29 апреля 2012 г.

NHibernate transactions – обязательны при всех операциях с базой данных.

  Соблазн не использовать транзакции всегда велик. Вроде бы и ресурсов они расходуют, и риск взаимоблокировок всегда велик. По этим причинам многие отказываются от транзакций. Но не стоит следовать этой практике. И сейчас я расскажу как минимум о трёх причинах, по которым транзакции следует использовать при любом обращении к базе данных через NHibernate.

Почему надо использовать транзакции.

  1. РСУБД транзакционны по своей природе. Даже если вы не создаёте транзакцию явно, СУБД сделает это сама. При этом  транзакцию создаётся на каждый оператор (statement), и риск взаимоблокировок сохраняется. Создание транзакции не бесплатный процесс, и требует ресурсов. Отсюда вытекает простая логика: меньше транзакций – меньше расходуется ресурсов сервера.

  2. Согласованность данных. Думаю, никому не надо объяснять – это основа основ работы с БД. Без использования транзакций в БД может попасть всякий мусор, влияние которого на поведение системы порой бывает трудно предсказать. Чтобы особенно болезненно если ссылочная целостность проверяется только на уровне приложения (например связи many-to-any в NHibernate). Всегда проще откатить транзакцию, нежели разбираться с битыми данными.

  3. Кеш второго уровня. NHibernate за кадром делает много работы, чтобы поддерживать кэш в актуальном состоянии. Отсутствие транзакций в коде заставляет NHibernate игнорировать кэш второго уровня, и всегда делать запрос в базу. Причина в неизвестности результата DML-операций: при обновлении базы без транзакции в случае ошибки база оказывается в недетерминированном состоянии. Поэтому NHibernate вынужден инвалидировать данные в кэше, и направлять запрос в базу. В этом легко убедиться. Посмотрим код:

[Test]
public void Query_cache_invalidated()
{
    using (var session = factory.OpenSession())
    //using (var tx = session.BeginTransaction())
    {
        session.SaveOrUpdate(new Cat {Name = "Bazil"});
        session.Flush();
        //  tx.Commit();
    }
    using (var session = factory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var reloadedPeter = session.Get<Cat>(50L);
        tx.Commit();
    }
    Console.WriteLine("HitCount: {0}", factory.Statistics.SecondLevelCacheHitCount);
}   

И вот что мы увидим в логах:


image


  Но стоит только снять комментарии с кода транзакции, и картина сразу меняется в лучшую сторону:


image


  Как видно из логов, в момент фиксации транзакции NHibernate кладёт в кэш изменённые данные, и в следующий раз даже не понадобится делать запрос в БД для загрузки. Поиск по ключу в кэше намного быстрее, чем запрос к БД.


  Полагаю, мне удалось убедить вас в необходимости использования транзакций. А теперь посмотрим на еще одну вещь, о которой стоит знать при использовании NHibernate с транзакциями.


Некоторые последствия.


  При использовании транзакций NHibernate старается обеспечивать согласованность данных. Давай рассмотри такой код:

[Test]
public void Sudden_insert_happened_on_read_operation2()
{
    using(var session = _sessionManager.OpenSession())
    using(var tx = session.BeginTransaction())
    {
        session.SaveOrUpdate(new Cat {Name = "Bazil"});
        tx.Commit();
    }
    using (var session = _sessionManager.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var peter = new Cat { Name = "Peter" };
        session.Persist(peter);
        var reloadedPeter = session.Get<Cat>(50L);
        reloadedPeter.Name = "Johnny";
        var totalCats = session
            .QueryOver<Cat>()
            .Where(x => x.Name != string.Empty)
            .RowCount();
    }
}

  В одной сессии создали сущность, и сохранили ее в базу, в другой добавили в сессию одну сущность, и изменили одно из свойств другой. Затем выполнили запрос. А теперь посмотрим, что же было в логах NHibernate?


image


    Я не делал ни Session.Flush(), ни tx.Commit(), но Nhibernate выполнил Insert и Update. Причина в том, что NHibernate понимает, что сделанные в сессии изменения могут повлиять на результат запроса. Поэтому предварительно приводит БД в согласованное с сессией состояние. Если убрать транзакцию, то предварительного обновления происходить не будет (всё равно БД будет в недетерминированном состоянии).


  Если еще не используете транзакции для работы с NHibernate – то самое время начать. Подмигивающая рожица

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

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