NHibernate ISession Flush:何时何地使用以及为什么?

190

有一件事让我非常困惑,那就是在使用session.Flush时,与session.Commitsession.Close结合使用。

有时候session.Close有效,例如,它会提交我需要的所有更改。 我知道当我有一个事务或者需要做多个创建/更新/删除操作时,我需要使用commit,这样如果出现错误,我可以选择回滚。

但有时我真的被session.Flush背后的逻辑所困扰。 我见过一些例子,其中包含session.SaveOrUpdate()然后跟着一个flush,但当我删除Flush时,它仍能正常工作。 有时候我在Flush语句上遇到了超时错误,删除它能确保我不会遇到这个错误。

有没有人有关于何时使用Flush的好准则? 我已经查看了NHibernate文档,但仍然找不到直接的答案。

4个回答

239

简要来说:

  1. 始终使用事务
  2. 不要使用 Close(),而是将您对 ISession 的调用包装在一个 using 语句内或在其他地方管理您的 ISession 生命周期

来自文档

间隔一段时间,ISession 将执行必要的 SQL 语句,以使 ADO.NET 连接状态与内存中持有的对象状态同步。默认情况下,此进程(刷新)在以下几点发生:

  • 从某些 Find()Enumerable() 调用中
  • NHibernate.ITransaction.Commit()
  • ISession.Flush()

SQL 语句的发出顺序如下:

  1. 所有实体插入,在相应的对象使用 ISession.Save() 保存时的相同顺序中
  2. 所有实体更新
  3. 所有集合删除
  4. 所有集合元素的删除、更新和插入
  5. 所有集合插入
  6. 所有实体删除,在相应的对象使用 ISession.Delete() 删除时的相同顺序中

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

除非您显式调用 Flush(),否则无法保证会话何时执行 ADO.NET 调用,只保证它们被执行的顺序。然而,NHibernate 确保 ISession.Find(..) 方法永远不会返回过期数据;它们也不会返回错误数据。

可以更改默认行为,以便刷新发生得更少。FlushMode 类定义了三种不同的模式:仅在提交时刷新(仅在使用 NHibernate ITransaction API 时),使用解释的常规程序自动刷新,或仅在显式调用 Flush() 时才刷新。最后一种模式适用于长时间运行的工作单元,在其中保持打开和断开连接的 ISession

...

还请参考此部分

结束会话包括四个不同的阶段:

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

刷新会话

如果您正在使用 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() 将其返回到池中。


3
对我来说,这行代码很关键:“Close() 的主要含义是 ADO.NET 连接将被会话放弃。” 如果您不调用 ISession.Close(),则连接会一直保持打开状态,直到出现数据库超时。 :o - Dave Thieben
通常我们会:打开会话 session.BeginTransaction() 工作... session.Transaction.Commit() session.BeginTransaction() 工作... session.Transaction.Commit() session.BeginTransaction() 工作... session.Transaction.Commit() 释放会话。 - Agile Jedi
精彩的写作,点赞等等 - 但是我认为可能需要进行编辑,因为你在顶部说“永远不要使用close”,然后在后面说“如果回滚事务,则应立即关闭并丢弃当前会话”。 - SpaceBison
SQL语句的顺序可以改变吗?我的意思是,我需要先对实体对象执行更新,然后再插入,因为在相应的表中有一个约束。 - bob_saginowski

15

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


1

有时,ISession将执行必要的SQL语句,以使ADO.NET连接的状态与内存中保存的对象的状态同步。

并且始终使用

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

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


0

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

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

在代码的结尾位置,你可以看到一段设置identity insert 的代码。我先设置了identity insert 为on,保存实体,flush之后再设置identity insert 为off。如果没有这个flush,在保存实体之前似乎会重复地设置identity insert 为on和off。
使用Flush()让我更好地控制了代码执行过程。
以下是另一个例子: 在TransactionScope内发送NServiceBus消息 我不完全理解为什么,但是使用Flush()防止了出现错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接