nHibernate-我是否必须对单个但被引用的更新使用事务?

nHibernate-我是否必须对单个但被引用的更新使用事务?,nhibernate,Nhibernate,以下是我的基本nHibernate实体: public class Patients { public virtual int Id { get; protected set; } public virtual string First_Name { get; set; } public virtual string Last_Name { get; set; } public virtual string Pesel { get; set; } publ

以下是我的基本nHibernate实体:

public class Patients
{
    public virtual int Id { get; protected set; }
    public virtual string First_Name { get; set; }
    public virtual string Last_Name { get; set; }
    public virtual string Pesel { get; set; }
    public virtual string Gender { get; set; }
    public virtual string Height { get; set; }   
    public virtual string Comments { get; set; }   

    public virtual Adresses Address { get; set; }
    public virtual Adresses CorrespondencyAddress { get; set; }

    public virtual StudyPayer DefaultPayer { get; set; }
    public virtual DateTime? BirthDate { get; set; }
    public virtual string PhoneNumber { get; set; }
    public virtual DateTime RegistrationDate { get; set; }  

    public virtual string Citizenship { get; set; }
    public virtual string Country_Region { get; set; }
    public virtual string EMail { get; set; }     
    public virtual string Doc_Id_Number { get; set; } 

}
以下是在患者中引用的实体地址:

public class Adresses
{
    public virtual int Id { get; protected set; }
    public virtual string Street { get; set; }
    public virtual string HomeNumber { get; set; }
    public virtual string PostalCode { get; set; }
    public virtual string City { get; set; }
    public virtual string Country { get; set; }
}
在我的代码中,我为患者添加了一个地址,然后我想插入这两个地址:

    Patients pacjent = new Patients();
    pacjent.First_Name = model.First_Name;
    pacjent.Last_Name = model.Last_Name;

    Adresses corAddress = new Adresses();
    corAddress.City = model.CityCorrespondency;
    corAddress.HomeNumber = model.HouseNumberCorrespondency;
    corAddress.PostalCode = model.PostCodeCorrespondency;
    corAddress.Street = model.StreetCorrespondency;

    pacjent.CorrespondencyAddress = corAddress;
在数据库层中,至少需要2次插入或更新

问题是我是否需要在事务中保存它:

using (var transaction = session.BeginTransaction())
{
    transaction.Begin();
    session.SaveOrUpdate(pacjent);
    transaction.Commit();
}
或者干脆由nHibernate负责交易:

session.SaveOrUpdate(pacjent);

如果您在事务之外使用SaveOrUpdate,它将自动提交—如果其中一条语句在提交之前使所有语句崩溃

对于显式事务,它应该能够正常工作,并且在出现任何异常时回滚。顺便说一句,你必须正确地映射地址,否则它不会被保存

另一点是,您不必调用transaction.Begin()-它是通过session.BeginTransaction()自动启动的


为了说明这一点,让我提供这些事实。NHibernate实际上是在ADO.NET之上构建的,正如我们在doc中所读到的:

。。。在ISession中,每个数据库操作都发生在隔离数据库操作(甚至是只读操作)的事务中。我们使用NHibernate的ITransaction API从底层事务策略(在我们的例子中是ADO.NET事务)中进行抽象

doc的另一个重要部分是:

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

  • 通过调用
    Find()
    Enumerable()
  • NHibernate.ITransaction.Commit()
  • ISession.Flush()

可以更改默认行为,以减少刷新的频率。FlushMode类定义了三种不同的模式:仅在提交时刷新(并且仅在使用NHibernate ITransaction API时),使用解释例程自动刷新(仅在显式NHibernate ITransaction内工作),或者除非显式调用
flush()
,否则从不刷新

要知道这一点很重要,NHibernate创建了名为
ISession
的抽象,它接受所有命令,如
Save()
SaveOrUpdate()
。。。但是将这些内容提取到INSERT和UPDATE中是在session.Flush()上完成的

上面我们已经看到,我们可以使用事务。但事实上,我们不必(非常糟糕的方法)。我们可以使用显式事务,它是围绕每个插入、更新。。。命令-但分开。让我们仔细看看这个:

让我举一个例子:

。。。默认情况下,数据库以显式事务模式运行,并启用自动提交事务。这实际上意味着除非使用BEGIN transaction启动显式事务,否则每次数据修改都会在语句后提交的单独事务中启动。这允许数据库在语句失败时回滚整个语句(例如大容量插入,或修改触发器中其他数据的插入)

这就是问题所在。如果我们不使用事务作为包装器,我们最终可能会得到不一致的状态。有些写操作可能会提交,有些可能会回滚

这就是为什么我们总是应该使用显式事务,即NHibernate事务API,以确保持久化是全部还是全部


如果你想拥有可靠的软件,除了使用事务,别无选择。其他任何事情都是危险的。你可以,但你不应该在没有事务的情况下调用WRITE…我知道ACID,但我的问题是nHibernate是否在这种特定情况下处理事务:一次调用SaveOrUpdate()但是对DB有2个插入。答案是否定的。NHibernate将使用隐式transactin,即每个语句都有自己的ACID解决方案。也就是说,如果不在显式事务中包装它,可能会发生一个插入或更新会成功,而另一个不会成功。因此,永远不要使用隐式的。始终在事务中包装它。而且,不要真正的插入更新与Save()无关,而是与session.Flush()有关……谢谢。我刚刚在实际情况中检查过:)很高兴看到这一点。我相信您将正确启动事务,并在完成请求后提交它。;)那会给你你需要的水果;)这两个代码现在都可以正常工作:)但是数据库至少有两个插入,所以如果其中一个插入失败怎么办?它是否会自动清除数据库中的第二个插入而不进行事务处理?映射是可以的。感谢其他建议…我已经在实际情况中检查了它,当在患者表上失败时,它在地址表中留下了插入的数据。但在事务中它工作正常吗?我要更正我的答案——很抱歉beein写错了一篇类似于Radim Köhler评论的回复,这是下面的问题。这是正确的答案:)
using (var transaction = session.BeginTransaction())
{
    session.SaveOrUpdate(pacjent);
    transaction.Commit();
}