nHibernate - 我是否必须将事务用于单个但引用的更新?

nHibernate - Do I have to use transaction for single but referenced update?

这是我的基本 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; } 

}

这里是 Patients 中引用的 Addresses 实体:

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()

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

为了清楚起见,让我提供这些事实。 NHibernate 实际上是建立在 ADO.NET 之上的,正如我们在文档中看到的那样,1.4. Playing with cats:

... In an ISession, every database operation occurs inside a transaction that isolates the database operations (even read-only operations). We use NHibernate's ITransaction API to abstract from the underlying transaction strategy (in our case, ADO.NET transactions)...

文档的另一个重要部分是:

9.6. Flush

From time to time the ISession will execute the SQL statements needed to synchronize the ADO.NET connection's state with the state of objects held in memory. This process, flush, occurs by default at the following points

  • from some invocations of Find() or Enumerable()
  • from NHibernate.ITransaction.Commit()
  • from ISession.Flush()

...

It is possible to change the default behavior so that flush occurs less frequently. The FlushMode class defines three different modes: only flush at commit time (and only when the NHibernate ITransaction API is used), flush automatically using the explained routine (will only work inside an explicit NHibernate ITransaction), or never flush unless Flush() is called explicitly...

知道这一点很重要,NHibernate 创建了称为 ISession 的抽象,它接受所有命令,如 Save()SaveOrUpdate() ... 但是将这些提取到 INSERT 和更新在 session.Flush().

完成

上面我们已经看到,我们可以使用事务。但实际上我们不必(很不好的做法)。我们可以使用显式事务,它是围绕每个 INSERT、UPDATE... 命令创建的——但是是分开的。让我们仔细看看这个:

Difference between Implicit and Explicit Transaction

让我引用其中一个 answers:

... By default the database operates in explicit transaction mode with autocommiting transactions enabled. That actually means that unless an explicit transaction is started using BEGIN TRANSACTION, every data modification is started in a separate transaction which is committed after the statement. That allows the database to rollback an entire statement when it fails (for instance a bulk insert, or an insert that modifies other data in a trigger).

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

And that's why we always should use explicit transaction, NHibernate transaction API, to be sure that persisted was ALL or NOTHING