首页 > 解决方案 > 为什么 NHibernate 在使用存储过程进行更新时不反映内存状态?

问题描述

我有一个过程,我有一个 NHibernate 会话,我用它来对数据库运行查询。然后,我遍历结果集合,并且对于每次迭代,使用相同的 NHibernate 会话,我调用 SQL 存储过程(使用CreateSQLQuery()& ExecuteUpdate()),最终对该实体的字段执行更新。

当它完成对列表的迭代(并调用 SP x 次)时,如果我直接在 SSMS 中检查数据库,我可以看到每行的 UPDATE 都已应用。

但是,在我的代码中,如果我随后立即再次运行相同的初始查询以检索该实体列表,则它不会反映 SP 对每一行所做的更新——该值仍然为 NULL。

我的应用程序中没有针对 NHibernate 的配置指定任何缓存行为,并且SetCacheMode()在调用查询时尝试了不同的缓存行为,但似乎没有任何区别 - 我可以直接在数据库中看到的值已经更新,当我重新查询(使用Session.QueryOver())数据库(使用同一个会话)时,没有被带回更新。

标签: asp.net-mvcnhibernate

解决方案


通过调用CreateSQLQuery(更新数据库,单行或多行无关紧要),实际上您正在执行DML 样式的操作,它不会更新内存中的状态。
任何调用CreateSQLQueryCreateQuery不会使用/反映跟踪。这些被认为超出了工作单元的范围。
这些操作直接影响底层数据库,忽略任何内存状态。

14.3. DML 风格的操作
正如已经讨论过的,自动和透明的对象/关系映射与对象状态的管理有关。这意味着对象状态在内存中可用,因此直接在数据库中操作(使用 SQL 数据操作语言 (DML) 语句:INSERT、UPDATE、DELETE)数据不会影响内存状态。然而,NHibernate 提供了通过 Hibernate 查询语言 (HQL) 执行的批量 SQL 样式 DML 语句执行的方法。也可以使用 Linq 实现。

他们(可能)处理大量数据。出于性能原因,它们在某些情况下是必需的。有了这些,跟踪就不起作用了;所以是的,内存状态变得无效。你必须小心使用它们。

如果我随后立即再次运行相同的初始查询以检索该实体列表,它不会反映 SP 为每一行所做的更新 - 该值仍然为 NULL。

这是由于第一(会话)级缓存。这始终默认启用,不能使用 禁用ISession

当您第一次加载对象时,它会命中数据库。您从数据库中获取对象-循环遍历它们-执行那些不在工作单元中的命令(如上所述)-再次执行相同的查询两次ISession以在同一实例下加载相同的对象。第二次调用根本没有命中数据库

它只是从内存中返回实例。由于您的内存中实例根本没有更新,因此您始终会获得原始实例。

要获取更新的实例,请关闭第一个会话并使用新会话重新加载实例。

更多细节请参考:Hibernate Query Cache 是如何工作的


推荐阅读