NHibernate ISession Flush:何时何地使用它,为啥?

Posted

技术标签:

【中文标题】NHibernate ISession Flush:何时何地使用它,为啥?【英文标题】:NHibernate ISession Flush: Where and when to use it, and why?NHibernate ISession Flush:何时何地使用它,为什么? 【发布时间】:2010-09-07 18:49:00 【问题描述】:

让我彻底困惑的一件事是 session.Flushsession.Commitsession.Close 结合使用。

有时session.Close 会起作用,例如,它会提交我需要的所有更改。我知道当我有一个事务或一个工作单元有多个创建/更新/删除时我需要使用提交,以便在发生错误时选择回滚。

但有时我真的被session.Flush 背后的逻辑所阻碍。我见过一些例子,你有一个session.SaveOrUpdate(),然后是一个刷新,但是当我删除 Flush 时它仍然可以正常工作。有时我在 Flush 语句中遇到错误,说会话超时,删除它可以确保我没有遇到该错误。

是否有人对何时何地使用 Flush 有很好的指导?我已经为此查看了 NHibernate 文档,但我仍然找不到一个简单的答案。

【问题讨论】:

【参考方案1】:

简单地说:

    始终使用事务 不要使用Close(),而是将调用封装在ISession 中的using 语句中,或者在其他地方管理ISession 的生命周期

来自the documentation:

ISession 将不时执行将 ADO.NET 连接状态与内存中保存的对象状态同步所需的 SQL 语句。这个过程,flush,默认发生在以下几点

来自Find()Enumerable() 的一些调用 来自NHibernate.ITransaction.Commit() 来自ISession.Flush()

SQL语句按以下顺序发出

    所有实体插入,以相同的顺序使用ISession.Save()保存相应的对象 所有实体更新 所有集合删除 所有集合元素的删除、更新和插入 所有集合插入 所有实体删除,使用ISession.Delete()删除相应对象的顺序相同

(一个例外是使用原生 ID 生成的对象在保存时被插入。)

除非您明确指出Flush(),否则绝对不能保证 Session 何时执行 ADO.NET 调用,只能保证它们的执行顺序。然而,NHibernate 确实保证ISession.Find(..) 方法永远不会返回过时的数据;他们也不会返回错误的数据。

可以更改默认行为以减少刷新频率。 FlushMode 类定义了三种不同的模式:仅在提交时刷新(并且仅在使用 NHibernate ITransaction API 时),使用解释的例程自动刷新,或者除非显式调用 Flush(),否则从不刷新。最后一种模式对于长时间运行的工作单元很有用,其中ISession 长时间保持打开和断开连接。

...

另请参考this section:

结束会话涉及四个不同的阶段:

刷新会话 提交事务 关闭会话 处理异常

刷新会话

如果您恰好使用ITransaction API,则无需担心这一步。它会在事务提交时隐式执行。否则,您应该调用ISession.Flush() 以确保所有更改都与数据库同步。

提交数据库事务

如果您使用的是 NHibernate ITransaction API,则如下所示:

tx.Commit(); // flush the session and commit the transaction

如果您自己管理 ADO.NET 事务,您应该手动 Commit() ADO.NET 事务。

sess.Flush();
currentTransaction.Commit();

如果您决定不提交更改:

tx.Rollback();  // rollback the transaction

或:

currentTransaction.Rollback();

如果你回滚事务,你应该立即关闭并丢弃当前会话,以确保 NHibernate 的内部状态是一致的。

关闭 ISession

ISession.Close() 的调用标志着会话的结束。 Close() 的主要含义是会话将放弃 ADO.NET 连接。

tx.Commit();
sess.Close();

sess.Flush();
currentTransaction.Commit();
sess.Close();

如果您提供了自己的连接,Close() 会返回对它的引用,因此您可以手动关闭它或将其返回到池中。否则 Close() 将其返回到池中。

【讨论】:

对我来说,这一行很关键:“Close() 的主要含义是会话将放弃 ADO.NET 连接。”如果你不调用 ISession.Close(),你的连接会被填满,直到你得到 db 超时。 :o 我们通常:打开会话 session.BeginTransaction() 工作... session.Transaction.Commit() session.BeginTransaction() 工作... session.Transaction.Commit() session.BeginTransaction()工作.. session.Transaction.Commit() 处理会话。 精彩的文章和 +1 等 - 但是我认为可能需要进行编辑,因为您在顶部说“从不使用关闭”,然后是“如果您回滚事务,您应该立即关闭并丢弃当前会话” SQL语句的顺序可以改变吗?我的意思是我需要对实体对象执行更新而不是插入,因为我在相应的表中有一个约束。【参考方案2】:

从 NHibernate 2.0 开始,DB 操作需要事务。因此,ITransaction.Commit() 调用将处理任何必要的刷新。如果由于某种原因您没有使用 NHibernate 事务,则不会自动刷新会话。

【讨论】:

【参考方案3】:

ISession 将不时执行同步 ADO.NET 连接状态与内存中对象状态所需的 SQL 语句。

并且总是使用

 using (var transaction = session.BeginTransaction())
 
     transaction.Commit();
 

在提交更改之后,我们使用 transaction.Commit(); 将更改保存到数据库中;

【讨论】:

【参考方案4】:

这是我的代码的两个示例,如果没有 session.Flush(),它将失败:

http://www.lucidcoding.blogspot.co.uk/2012/05/changing-type-of-entity-persistence.html

在此结束时,您可以看到一段代码,其中我设置了身份插入,保存实体然后刷新,然后设置身份插入关闭。如果没有这个刷新,它似乎是在打开和关闭身份插入,然后保存实体。

使用 Flush() 让我可以更好地控制正在发生的事情。

这是另一个例子:

Sending NServiceBus message inside TransactionScope

我不完全理解为什么会这样,但是 Flush() 阻止了我的错误发生。

【讨论】:

以上是关于NHibernate ISession Flush:何时何地使用它,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

NHibernate Isession管理

当 CacheMode=Ignore 时,为啥 NHibernate.ISession.CreateQuery 会返回与 CreateSQLQuery 不同的东西?

如何让 NHibernate ISession 缓存主键未检索到的实体

会话已关闭对象名称:'ISession'。在 NHibernate.Impl.AbstractSessionImpl.ErrorIfClosed() - 如何阻止会话过早关闭

Nhibernate in asp,net ISession 帮助

NHibernate ISession 与 Castle Windsor IoC 的循环依赖