【问题标题】:Saving child collections with NHibernate使用 NHibernate 保存子集合
【发布时间】:2016-08-29 21:34:26
【问题描述】:

我正在学习 NHibernate,所以请耐心等待。

我有一个 Order 类和一个 Transaction 类。订单与交易有一对多的关联。我数据库中的事务表对 OrderId 外键有非空约束。

订单类:

    public class Order {
    public virtual Guid Id { get; set; }
    public virtual DateTime CreatedOn { get; set; }
    public virtual decimal Total { get; set; }

    public virtual ICollection<Transaction> Transactions { get; set; }

    public Order() {
        Transactions = new HashSet<Transaction>();
    }
}

订单映射:

  <class name="Order" table="Orders">
<cache usage="read-write"/>
<id name="Id">
  <generator class="guid"/>
</id>
<property name="CreatedOn" type="datetime"/>
<property name="Total" type="decimal"/>
<set name="Transactions" table="Transactions" lazy="false" inverse="true">
  <key column="OrderId"/>
  <one-to-many class="Transaction"/>
</set>

事务类:

    public class Transaction {
    public virtual Guid Id { get; set; }
    public virtual DateTime ExecutedOn { get; set; }
    public virtual bool Success { get; set; }

    public virtual Order Order { get; set; }
}

事务映射:

  <class name="Transaction" table="Transactions">
<cache usage="read-write"/>
<id name="Id" column="Id" type="Guid">
  <generator class="guid"/>
</id>
<property name="ExecutedOn" type="datetime"/>
<property name="Success" type="bool"/>
<many-to-one name="Order" class="Order" column="OrderId" not-null="true"/>

我真的不想要双向关联。我的交易对象不需要直接引用他们的订单对象(我只需要访问订单的交易)。但是,我必须添加它以便 Order.Transactions 持久保存到数据库中:

存储库:

        public void Update(Order entity)
    {
        using (ISession session = NHibernateHelper.OpenSession()) {
            using (ITransaction transaction = session.BeginTransaction()) {
                session.Update(entity);

                foreach (var tx in entity.Transactions) {
                    tx.Order = entity;
                    session.SaveOrUpdate(tx);
                }
                transaction.Commit();
            }
        }
    }

我的问题是,这将为订单集合上的每笔交易发布更新(无论它是否已更改)。

我试图解决的是必须在保存订单之前显式保存交易,而不是只需将交易添加到订单然后保存订单:

        public void Can_add_transaction_to_existing_order()
    {
        var orderRepo = new OrderRepository();
        var order = orderRepo.GetById(new Guid("aa3b5d04-c5c8-4ad9-9b3e-9ce73e488a9f"));

        Transaction tx = new Transaction();
        tx.ExecutedOn = DateTime.Now;
        tx.Success = true;

        order.Transactions.Add(tx);

        orderRepo.Update(order);
    }

虽然我发现了很多关于建立一对多关联的文章,但其中大部分都讨论了检索数据而不是持久化数据。

非常感谢, 本

【问题讨论】:

    标签: nhibernate nhibernate-mapping


    【解决方案1】:

    您需要在映射上设置级联属性,以便将持久性级联到子对象:

    <set name="Transactions" table="Transactions" lazy="false" inverse="true" cascade="all-delete-orphan">
    

    您的 Order 对象应该有一个 AddTransaction 方法,用于设置子级的父级引用。比如:

    public void AddTransaction(Transaction txn)
    {
        txn.Order = this;
        Transactions.Add(txn);
    }
    

    这将导致在 Order 被持久化时 Transaction 对象被持久化。您可以使用 internal 修饰符在 Transaction 上公开 Order 属性,使其不公开可见。

    【讨论】:

    • @Jamie - 这确实有效,尽管它看起来仍然像为属于订单的每个事务执行更新查询。我想我认为 NH 会弄清楚发生了什么变化,并且只为任何新事务发出插入,并为任何已更改的事务发出更新。也许我要求太多了? :)
    • 不,它应该是这样工作的。您可能有一个“幽灵”问题,即一个值被意外更改。在事务映射中设置 dynamic-update="true" 并查看正在发出的 SQL(通过分析器或日志记录)。你可能会发现你的对象无意中改变了一个值。
    • @Jamie 我已经查看了生成的 SQL,并且我没有更改现有事务的任何值。我的示例代码检索现有订单,向订单添加新交易,然后保存订单。如果我为该订单执行了四个现有事务,则执行的总 SQL 如下 1. select * from order where id = blah 2. select * from transactions where orderId = blah 3. 插入事务(新事务) 4. 更新订单(设置值与原始值相同) 5. 更新事务 1,2,3,4(将值设置为与以前相同)
    • 这很奇怪。即使您包含dynamic-update="true",它是否会更新所有列?我几乎总是使用初始化为 List 实现的 bag/IList,因此它可能与使用初始化为 HashSet 的 set/ICollection 相关。
    • @Jamie - 是的,我设置了 dynamic-update="true" 并且它仍然为每笔交易发布更新。我将 set/hashset 换成了 bag/ICollection 并遇到了同样的问题。你有一个基本的例子(或一个链接)这应该如何工作,也许我可以查看我自己的代码。现在只使用 NH 2 天,所以很难分辨什么是错误,什么是设计的 :(
    猜你喜欢
    • 2011-12-04
    • 2010-10-06
    • 1970-01-01
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多