【问题标题】:nHibernate - Do I have to use transaction for single but referenced update?nHibernate - 我必须对单个但引用的更新使用事务吗?
【发布时间】:2015-01-09 15:31:59
【问题描述】:

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

【问题讨论】:

  • 如果你想拥有可靠的软件,除了使用ACID事务之外别无选择。其他任何事情都将永远是危险的。可以,但不应该在没有事务的情况下调用 WRITE...
  • 我知道 ACID,但我的问题是 nHibernate 是否会在这种特定情况下处理事务:一次调用 SaveOrUpdate() 但两次插入数据库。
  • 答案是否定的。 NHibernate 将使用隐式事务处理,即每个语句都有自己的 ACID 解决方案。也就是说,如果您不将其包装在 EXPLICIT 事务中,则可能会发生一个 INSERT 或 UPDATE 会成功,而另一个则不会。所以,永远不要使用隐含的。始终将其包装在事务中。更重要的是,真正的 INSERT UPDATE 与 Save() 无关,而是与 session.Flush()...
  • 谢谢。我只是在实际情况下检查过:)
  • 很高兴看到这一点。我相信您会在 COMPLETE 请求结束时正确启动事务并提交它。 ;) 这会给你你需要的水果 ;)

标签: nhibernate


【解决方案1】:

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

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

doc的另一个重要部分是:

9.6. Flush

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

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

...

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

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

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

Difference between Implicit and Explicit Transaction

让我引用answers 之一:

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

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

这就是为什么我们总是应该使用显式事务,NHibernate 事务 API,以确保持久化是 ALL 或 NOTHING

【讨论】:

    【解决方案2】:

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

    使用显式事务,它应该可以正常工作并在出现异常时回滚。顺便说一句,您必须正确映射地址 - 否则不会保存。

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

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

    【讨论】:

    • 两个代码现在都可以正常工作:) 但是至少有 2 个插入到 DB 那么如果其中一个会失败怎么办?如果它会自动从数据库中清除第二次插入而不进行事务处理?映射没问题。感谢其他建议...
    • 我已经在实际情况下进行了检查,当在 Patient 表上失败时,它在 Address 表中留下了插入的数据。
    • 但是通过交易它可以正常工作吗?我将更正我的答案 - 对不起,我错了
    • 写一个类似于 Radim Köhler 评论的答案。这是正确的答案:)
    猜你喜欢
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-05
    • 1970-01-01
    相关资源
    最近更新 更多