代码之家  ›  专栏  ›  技术社区  ›  Shachar

nhibernate session.flush()失败,但进行了更改

  •  0
  • Shachar  · 技术社区  · 15 年前

    我们有一个SQL Server数据库表,该表由用户ID、一些数值(如余额)和版本列组成。

    我们有多个线程并行更新这个表的值列,每个线程都在自己的事务和会话中(我们使用的是每个线程的会话模型)。因为我们希望所有逻辑事务都发生,所以每个线程都会执行以下操作:

    1. 加载当前行(映射到类型)。
    2. 根据旧值更改值。(例如,添加50)。
    3. 会话更新(obj)
    4. session.flush()(由于我们是乐观的,所以我们希望在更新之前确保有正确的版本值)
    5. 如果步骤4(刷新)引发StaleStateException,请刷新对象(使用lockMode.read)并转到步骤1。

    对于每个逻辑事务,我们只执行特定次数的操作,如果在x次尝试之后无法提交,我们将拒绝逻辑事务。

    每一个这样的线程都会定期提交,例如在100个成功的逻辑事务之后,以将由提交引起的I/O保持在可管理的级别。意思是-我们有一个单独的数据库事务(每个事务)有多个刷新,每个逻辑更改至少一次。

    你问,这里有什么问题?嗯,在提交时,我们看到对失败逻辑对象的更改。 具体来说,如果我们在执行步骤1时(第一次)的值是50,并且我们试图将其更新为100(但由于另一个线程将其更改为70而失败),则该行的值为50。显然这是错误的。

    我们这里缺少什么?

    4 回复  |  直到 9 年前
        1
  •  2
  •   Chris Shaffer    15 年前

    嗯,我在这里没有很多经验,但有一件事我记得读过 in the documentation 如果发生异常,您应该立即回滚事务并释放会话。也许您的问题与会话处于不一致状态有关?

    另外,在您的代码中调用更新是 not necessary . 因为您在该会话中加载了对象,所以nhibernate已经在跟踪它。

        2
  •  1
  •   FelixM    15 年前

    如果您仍然想进行更改,那么为什么还要麻烦行版本控制呢?如果您总是简单地更新数据并让最后一个事务获胜,那么听起来您应该得到相同的结果。

    至于为什么更新会成为永久性的,这取决于版本检查/更新的SQL语句是什么样子的,以及您的事务控制,这是您在代码示例中遗漏的。如果打开Hibernate SQL日志记录,则很可能会发现这是如何发生的。

        3
  •  0
  •   Arnis Lapsa    15 年前

    我不是一个无血气的古鲁,但答案似乎很简单。

    当nhibernate加载一个对象时,它希望只要它在nhibernate会话缓存中,就不会在db中发生更改。

    正如你提到的-你有多线程应用程序。

    这就是发生的事情=>

    • 第一个线程加载实体
    • 第二个线程加载实体
    • 第一个线程更改实体
    • 第二线程更改实体 =>发现加载的实体已被其他东西更改,并且担心它已将更改搞砸了第一个线程-引发一个异常以让程序员意识到这一点。

    您缺少锁定机构。对于如何正确、优雅地应用这一点,我们说不出多少。也许吧 Transaction 会有帮助的。

    当我们同时使用nhibernate和raw ado.net时,也遇到了类似的问题(幸运的是,仅用于查询,至少用于生产代码)。我们所要做的就是在插入/更新时强制更新数据库,这样我们就可以通过全文搜索来查询某些特定的实体。

    StaleStateException 在集成测试中,我们使用原始ADO.NET重置DB。NHibernate会话通过一系列的测试是活跃的,但是每个测试都试图在没有意识到NHibernate的情况下清理数据库。

        4
  •  0
  •   hazzik    9 年前