NHibernate: эксклюзивная блокировка - PullRequest
14 голосов
/ 22 апреля 2009

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

Прямо сейчас у меня есть этот код:

With.Transaction (session, IsolationLevel.Serializable, delegate
{
    ICriteria crit = session.CreateCriteria (typeof (TarificationProfile));

    crit.SetLockMode (LockMode.Upgrade);

    crit.Add (Expression.Eq ("Id", tarificationProfileId));

    TarificationProfile profile = crit.UniqueResult<TarificationProfile> ();

    nextNumber = profile.AttestCounter;

    profile.AttestCounter++;

    session.SaveOrUpdate (profile);
});

Как видите, я установил для LockMode для этого критерия значение «Upgrade». Это выдает оператор SQL для SQL Server, который использует подсказки блокировки updlock и rowlock:

SELECT ... FROM MyTable with (updlock, rowlock)

Однако я хочу использовать настоящую эксклюзивную блокировку. То есть, не позволяйте другим читать эту самую запись, пока я не сниму блокировку. Другими словами, я хочу использовать подсказку блокировки xlock вместо updlock.

Я не знаю, как (или даже если) я могу достичь этого ... Может быть, кто-то может дать мне несколько советов по этому поводу:)

Если это действительно необходимо, я могу использовать функциональность SQLQuery NHibernate и написать свой собственный SQL-запрос, но я бы хотел как можно больше этого избегать.

Ответы [ 4 ]

7 голосов
/ 11 февраля 2010

HQL DML-запрос выполнит обновление без блокировки.

Это доступно в NHibernate 2.1, но пока отсутствует в справочной документации. Документация по Java hibernate очень близка к реализации NHibernate.

Предполагая, что вы используете ReadCommitted Isolation, вы можете безопасно прочитать свое значение внутри транзакции.

With.Transaction (session, IsolationLevel.Serializable, delegate
{
    session.CreateQuery( "update TarificationProfile t set t.AttestCounter = 1 + t.AttestCounter where t.id=:id" )
        .SetInt32("id", tarificationProfileId)
        .ExecuteUpdate();

    nextNumber = session.CreateQuery( "select AttestCounter from TarificationProfile where Id=:id" )
        .SetInt32("id", id )
        .UniqueResult<int>();
}

В зависимости от имен вашей таблицы и столбца сгенерированный SQL будет:

update TarificationProfile
set    AttestCounter = 1 + AttestCounter
where  Id = 1 /* @p0 */

select tarificati0_.AttestCounter as col_0_0_
from   TarificationProfile tarificati0_
where  tarificati0_.Id = 1 /* @p0 */
3 голосов
/ 11 февраля 2010

Я сомневаюсь, что это можно сделать из NHibernate. Лично я бы использовал хранимую процедуру, чтобы сделать то, что вы пытаетесь выполнить.

Обновление: Учитывая продолжающиеся отрицательные голоса, я остановлюсь на этом. Фредерик спрашивает, как использовать подсказки блокировки, которые являются подробностями синтаксиса и реализации его базового ядра базы данных, из своего уровня ORM. Это неправильный уровень для попытки выполнить такую ​​операцию - даже если бы это было возможно (это не так), вероятность того, что она когда-либо будет работать согласованно во всех базах данных, поддерживаемых NHibernate, исчезающе мала.

Здорово, что окончательное решение Фредерика не требовало упреждающих эксклюзивных блокировок (которые снижают производительность и, как правило, являются плохой идеей, если вы не знаете, что делаете), но мой ответ верен. Любой, кто сталкивается с этим вопросом и хочет сделать исключительную блокировку при чтении из NHibernate - во-первых: не надо, во-вторых: если вам нужно, используйте хранимую процедуру или SQLQuery.

1 голос
/ 06 февраля 2010

Если все, что вы читаете, выполняется с помощью IsolationLevel из Serializable, а все записи также выполняются с помощью IsolationLevel из Serializable, я не понимаю, зачем вам нужно самостоятельно блокировать строки базы данных.

Таким образом, сериализация сохраняет данные в безопасности, теперь у нас все еще есть проблема возможных мертвых блокировок ....

Если взаимоблокировки встречаются не часто, достаточно просто поместить [запустить транзакцию, прочитать, обновить, сохранить] в цикл повторных попыток, когда вы получите взаимоблокировку.

В противном случае простой оператор «выбрать для обновления», сгенерированный напрямую (например, не с помощью nhibernate), можно использовать для остановки другой транзакции, считывающей строку до ее изменения.

Однако я продолжаю думать, что, если частота обновления достаточно высока, чтобы получить много взаимоблокировок, ORM может не быть подходящим инструментом для обновления, или может потребоваться изменить схему базы данных, чтобы избежать значения, которое должно быть прочитано / записано (например, вычисление при чтении данных)

0 голосов
/ 22 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...