代码之家  ›  专栏  ›  技术社区  ›  Frederik Gheysels

NHibernate:专用锁

  •  14
  • Frederik Gheysels  · 技术社区  · 16 年前

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

    如您所见,我将此条件的锁定模式设置为“升级”。 这将为使用 updlock rowlock 锁定提示:

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

    但是,我希望能够使用真正的独占锁。也就是说,在我释放锁之前,防止其他人读取这个非常相同的记录。 换句话说,我希望能够使用 xlock 锁定提示,而不是 更新锁定 .

    我不知道如何(甚至是如果)我能做到…也许有人能给我一些提示:)

    如果确实有必要,我可以使用nhibernate的sql query功能,并编写自己的SQL查询,但我希望尽可能避免这种情况。

    4 回复  |  直到 13 年前
        1
  •  7
  •   Lachlan Roche    15 年前

    HQLDML查询将在不需要锁的情况下完成更新。

    这在NHibernate 2.1中可用,但在参考文件中还没有。爪哇 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 */
    
        2
  •  3
  •   Sam    13 年前

    我怀疑它可以从NHiberinate做。就个人而言,我将使用存储过程来完成您要完成的工作。

    更新: 考虑到持续下降的票数,我将进一步讨论这个问题。Frederick正在从他的ORM层询问如何使用锁定提示,这些提示是语法和底层数据库引擎的具体实现细节。这是尝试执行此类操作的错误级别-即使可能(但不可能),它在所有受NHibernate支持的数据库中持续工作的可能性也非常低。

    很好,弗雷德里克的最终解决方案不需要先发制人的专用锁(这会破坏性能,通常是个坏主意,除非你知道你在做什么),但我的回答是有效的。任何遇到此问题并希望对从nhibernate读取进行独占锁定的人-首先:不要这样做,其次:如果必须这样做,请使用存储过程或sqlquery。

        3
  •  1
  •   Ian Ringrose    15 年前

    如果您所读的全部内容都是用可序列化的isolationlevel完成的,并且所有的写入操作都是用可序列化的isolationlevel完成的,那么我不明白为什么您需要自己对数据库行进行任何锁定。

    所以序列化可以保证数据的安全,现在我们仍然有可能出现死锁的问题……

    如果死锁不是常见的,那么在获得死锁时将[开始事务、读取、更新、保存]放在重试循环中就足够了。

    否则,可以使用直接生成的用于更新的简单__select语句(例如,不与nhibernate一起生成)来停止另一个事务在更改行之前读取该行。

    不过,我一直在想,如果更新速度足够快,导致出现很多死锁,那么ORM可能不是更新的正确工具,或者数据库模式可能需要重新设计,以避免必须读/写的值(例如,在读取数据时计算它)。

        4
  •  0
  •   Stefan Steinegger    15 年前

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