2009-04-22 111 views
14

在NHibernate中,我想检索一个实例,并在表示数据库检索实体的记录上放置一个排它锁。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此标准,以“升级”。 此问题的SQL Server的SQL语句,它使用updlockrowlock锁提示:

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

我不知道如何(或者即使)我能做到这一点....也许有人可以给我这个:)

如果确实有必要,我可以使用功能的SQLQuery一些提示的NHibernate,并写我自己的SQL查询,但是,我想尽可能避免这种情况。

+0

你为什么要实现独占锁?可能还有其他解决方案。 – 2009-04-22 08:38:01

+0

为什么?因为它是必要的...我的实体包含应使用一个计数器,我必须绝对确保该计数器正确使用... – 2009-04-22 08:58:56

+0

你不应该读书的时候锁? – 2009-04-22 09:09:57

回答

7

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

这是NHibernate的2.1推出,但尚未引用文档。 Java hibernate documentation非常接近NHibernate的实现。

假设你正在使用READCOMMITTED隔离,你就可以安全地读取你的价值回的事务中。

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 */ 
0

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

1

如果你读的所有内容都是用Serializable的IsolationLevel完成的,而且所有的写操作都是用IsolationLevel的Serializable完成的,我不明白你为什么需要对你自己的数据库行进行任何锁定。

所以连载保持数据的安全,现在我们还是有可能的死锁问题....

如果死锁是不常见的,只是把[开始交易,读取,更新,保存]在发生死锁时重试循环可能已经足够了。

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

但是我一直在想,如果更新速度足够快以至于得到大量死锁,ORM可能不是正确的更新工具,或者数据库模式可能需要重新设计以避免必须读取的值/编写(例如,在读取数据时计算它)

3

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

更新:鉴于继续downvotes我会扩大这一点。 Frederick要求他的ORM层使用锁定提示,这是他底层数据库引擎的语法和实现特定细节。这是尝试执行此类操作的错误级别 - 即使可能(不是),它在所有NHibernate支持的数据库中始终如一地工作的可能性也极低。

这是伟大的弗雷德里克的最终解决方案并不需要先发制人的排他锁(它会杀死性能,除非你知道你在做什么通常是一个坏主意),但我的答案是有效的。任何人在这个问题上磕磕绊绊,想要从NHibernate上独家锁定读取 - 第一:不要,其次:如果你必须使用存储过程或SQLQuery。