NHibernate:独家锁定

时间:2009-04-22 08:09:29

标签: nhibernate locking

在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);
});

如您所见,我将此Criteria的LockMode设置为“Upgrade”。 这会为SQL Server发出一条SQL语句,该语句使用updlockrowlock锁定提示:

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

但是,我希望能够使用真正的独占锁。也就是说,防止其他人可以读取这个相同的记录,直到我释放锁定。 换句话说,我希望能够使用xlock锁定提示,而不是updlock

我不知道(或者甚至是否)我能做到这一点......也许有人可以给我一些暗示:)

如果真的有必要,我可以使用NHibernate的SQLQuery功能,并编写自己的SQL查询,但是,我想尽可能地避免这种情况。

4 个答案:

答案 0 :(得分:7)

HQL DML查询将完成您的更新,而无需锁定。

这在NHibernate 2.1中可用,但尚未在参考文档中。 Java hibernate documentation非常接近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 */

答案 1 :(得分:3)

我怀疑它可以从NHibernate完成。就个人而言,我会使用存储过程来完成你想要完成的任务。

更新:鉴于持续的下跌,我会对此进行扩展。 Frederick正在询问如何使用锁定提示,这是他的ORM层的基础数据库引擎的语法和实现特定细节。这是尝试执行此类操作的错误级别 - 即使可能(实际上不可能),它在所有NHibernate支持的数据库中始终如一地工作的可能性也非常低。

这是伟大的弗雷德里克的最终解决方案不需要先发制人的独家锁(这会破坏性能,除非你知道你在做什么,否则通常是一个坏主意),但我的回答是有效的。任何偶然发现这个问题并且想要从NHibernate进行独占锁定读取的人 - 首先:不要,其次:如果必须,请使用存储过程或SQLQuery。

答案 2 :(得分:1)

如果您所读取的所有内容都是使用Serializable的IsolationLevel完成的,并且所有的写入也都是使用Serializable的IsolationLevel完成的,那么我不明白为什么您需要自行锁定数据库行。

因此序列化保持数据安全,现在我们仍然存在可能死锁的问题....

如果死锁不常见,只需在遇到死锁时将[start transaction,read,update,save]置于重试循环中就足够了。

否则,直接生成的简单“select for update”语句(例如,不使用nhibernate)可用于在更改之前停止读取该行的另一个事务。

但是我一直在想,如果更新速度足够快以获得大量死锁,则ORM可能不是更新的正确工具,或者数据库架构可能需要重新设计以避免必须读取的值/书面(例如,在阅读数据时计算)

答案 3 :(得分:0)

如果要确保在事务期间从数据库中读取的值不会更改,则可以使用隔离级别“可重复读取”。但是你必须在所有关键交易中做到这一点。或者您通过升级锁将其锁定在关键阅读事务中。