在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语句,该语句使用updlock
和rowlock
锁定提示:
SELECT ... FROM MyTable with (updlock, rowlock)
但是,我希望能够使用真正的独占锁。也就是说,防止其他人可以读取这个相同的记录,直到我释放锁定。
换句话说,我希望能够使用xlock
锁定提示,而不是updlock
。
我不知道(或者甚至是否)我能做到这一点......也许有人可以给我一些暗示:)
如果真的有必要,我可以使用NHibernate的SQLQuery功能,并编写自己的SQL查询,但是,我想尽可能地避免这种情况。
答案 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)
如果要确保在事务期间从数据库中读取的值不会更改,则可以使用隔离级别“可重复读取”。但是你必须在所有关键交易中做到这一点。或者您通过升级锁将其锁定在关键阅读事务中。