我需要在NHibernate的工作单元中执行两个操作:事务性操作(实体保存)和非事务性操作。
由于非事务性操作无法回滚,如果我在执行非事务性操作(并最终提交事务)之前保存实体,我仍然会收到事务性行为:
问题:使用如下代码,NHibernate将不会执行实际的sql插入,直到调用transaction.Commit()(在内部调用session.Flush()):
using (var transaction = session.BeginTransaction())
{
session.Save(entity);
NotTransactionalOperationBasedOn(entity);
transaction.Commit(); // the actual sql insert will be executed here
}
使用这样的代码,如果Sql Insert失败则为时已晚:NotTransactionalOperation已被执行。要在执行NotTransactionalOperation之前执行实际的SQL插入,我必须在session.Save()之前明确调用session.Flush():
using (var transaction = session.BeginTransaction())
{
session.Save(entity);
session.Flush(); // the actual sql insert will be executed here
NotTransactionalOperationBasedOn(entity);
transaction.Commit();
}
代码有效,但......在提交事务之前调用session.Flush()是最佳做法吗?如果没有,是否有更好的方法来实现相同的结果?
答案 0 :(得分:3)
Flushing意味着NHibernate将确保所有更改都持久保存到DB。也就是说,它将确保执行所有必需的SQL语句。 当事务失败并因此回滚时,所有这些更改都将被还原。所以,我认为没有问题(你必须记住,你的实体可能没有处于有效状态,因为交易失败了。)
但是......我实际上没有看到问题: 当非事务性过程失败时,问题是什么?由于该程序是非事务性的,我想它对数据库所持有的信息没有影响?或者,这段代码做了什么?
如果是非交易性的,为什么不能在工作单元之外进行?
当保存失败时,我猜你会确保不仅事务被回滚,而且异常也将被抛出到堆栈上,直到它遇到错误处理程序,这可能意味着你的非交易代码也不会被执行:
using( var transaction = session.BeginTransaction() )
{
repository.Save (entity);
transaction.Commit(); // error, exception is thrown.
}
NonTransactionalCode (entity); // this line will not be executed, since the exception will be thrown up the stack until a suitable catch block is encountered.
答案 1 :(得分:1)
这是不寻常的,但是如果你想在工作单元之外做某些事情,这取决于内部完成的事情,那就完全有效了。然而,问题就变成了,如果操作单元依赖于内部的某些操作,为什么操作在工作单元之外执行?如果它完全依赖于顺序,那么在执行非事务性操作之前,您应该考虑刷新AND提交。如果非trans操作确定整个事物是否将提交(例如,它可能抛出异常),那么它应该是工作单元的一部分。
非trans选项,可能不应该依赖于DB中的数据,除非它依赖于像触发器这样的数据层代码(虽然我喜欢触发器,但是出于几个原因通常应该避免使用它们,其中最重要的是有这种复杂的保存操作的倾向)。除了作为最终选项之外,您通常应该将业务逻辑保留在应用程序层中。如果对象已保存到会话中,则该会话所在的任何位置都可以使用该数据,并且该会话应该根据需要进行广泛确定(或者考虑与可以访问父级的子级的共享“父”会话)所有时间)。