如何映射一对多 nhibernate class 映射

How to Map One-To-Many nhibernate class mapping

这是我的订单和 orderItem 类别:

public class Order : AggregateRootBase<OrderId>
{
    public string CustomerName { get; private set; }
    public IList<OrderItem> Items { get; set; }
    public DateTime RegisterDatetime { get; private set; }
}

public class OrderItem : ValueObjectBase
{
    public long Id { get; private set; }
    public long OrderId { get; set; }
    public long Number { get; private set; }
    public long Goods { get; private set; }
    public double UnitPrice { get; private set; }
}

我正在使用 nhibernate 作为我的 orm。在映射此代码时,我希望订单位于 Orders table 中,而 orderItem 存储在名为 OrderItems 的 diffrente table 中。 这是我的映射:

public class OrderMapping : ClassMapping<Order>
{
    public OrderMapping()
    {
        Table("Orders");
        Lazy(false);
        ComponentAsId(a => a.EntityId, a => { a.Property(x => x.DbId, x => x.Column("Id")); });

        Property(a=>a.CustomerName);            
        Property(a => a.RegisterDatetime);

        Bag(a => a.Items,
            mapper => {
                mapper.Inverse(true);
                mapper.Cascade(Cascade.None);
                mapper.Table("OrderItems");
                mapper.Key(k => k.Column(columnMapper => columnMapper.Name("OrderId")));
            },
            relation => { relation.OneToMany(); });
    }
}




public class OrderItemMapping : ClassMapping<OrderItem>
{
    public OrderItemMapping()
    {
        Lazy(false);
        Id(a => a.Id);
        Table("OrderItems");

        Property(a => a.OrderId);
        Property(a => a.Number);
        Property(a => a.Goods);
        Property(a => a.UnitPrice);

    } 
}

我也在数据库中创建了 tables,但是当我插入带有 3 个 orderItems 的订单时,它插入订单而不是 orderitems 感谢您的帮助

您已将父 Order 映射为 mapper.Inverse(true); Items bag,这告诉 NHibernate 您不希望父映射此关系。

由于子 OrderItem 没有到父的映射,因此没有任何关系保存。写集合映射时,至少有一侧必须是inverse(false).


您还设置了 mapper.Cascade(Cascade.None);,它告诉 NHibernate 您不希望父级 OrderItems 发生变化时处理对 Items 的任何操作 Session.

因此,除非您在每个 Item 上显式调用 Save(),否则它们将永远不会按原样保存。


在 NHibernate 中,自由 class 布局和最佳数据库性能之间存在折衷(尽管在这种情况下非常小)。

如果你真的不希望 OrderItem 有一个 Order 属性 链接回它的父级,那么你将得到一个额外的更新调用 SQL 每当 OrderItem 的父 Order 发生变化时,如果 OrderItems 的创建少于您对它们进行的操作的 ~10%,则此成本实际上可以忽略不计。

在这种情况下,您可以将 OrderMapping 上的 inverse(false) 设置为 Items

但我的建议是给 OrderItem 一个 Order 字段或 属性(您可以使用 NHibernate 映射一个私有字段!)然后给 OrderItemMapping使用 inverse(false) 返回父级的映射,以便在保存子级时,他们将处理关系。不过,您必须确保每个 OrderItem 在保存之前都填写了 Order field/property!

您可以使用 OrderId 属性 而不是对 Order 的完整引用来解决这个问题,但您必须查一下。


至于将它们保存到数据库中,最简单的方法是将mapper.Cascade(Cascade.None);更改为mapper.Cascade(Cascade.AllDeleteOrphan);(可能不是确切的class名称)。这将确保无论何时修改 Order 上的集合,然后 Save()/Update() Order,[=12= 中的所有 OrderItem ] 集合将相应地在数据库中更新。

您还可以检查不太严格的 Cascade 或根据当前设置的要求手动保存它们。


最后查看 Nhibernate 文档中的 bagset,我怀疑你在这里想要一个 set,我只会使用 baginverse(true) 曾经。如果你使用 inverse(false) bag 会有性能损失,除非可以识别项目(你的 OrderItem 有一个 Id,所以它可以!),但是如果项目可以识别,那为什么不把它做成set!

你有一个 inverse(false) bag 和可识别项目的唯一原因是,如果这些标识符与数据库中的主键不相关,则不会出现这种情况经常(或者在我的情况下!)。

谢谢@starlight54 但我的问题是这样解决的。 我不得不像这样改变我的 classes:

public class Order : AggregateRootBase<OrderId>
{
    private readonly IList<OrderItem> _items;
    public string CustomerName { get; private set; }
    public DateTime RegisterDatetime { get; private set; }
    public IReadOnlyCollection<OrderItem> Items => new ReadOnlyCollection<OrderItem>(_items);
}
public class OrderItem : ValueObjectBase
{
    private readonly long _number;
    private readonly long _goods;
    private readonly double _unitPrice;

    public long Number => _number;

    public long Goods => _goods;
    public double UnitPrice => _unitPrice;
}

请注意,在订购商品时,您必须创建字段,如 _number、_goods & ... 这是订单的映射:

public class OrderMapping : ClassMapping<Order>
{
    public OrderMapping()
    {
        Table("Orders");
        Lazy(false);
        ComponentAsId(a => a.EntityId, a => { a.Property(x => x.DbId, x => x.Column("Id")); });

        Property(a => a.CustomerName);
        Property(a => a.RegisterDatetime);

        IdBag(a => a.Items, map => {
            map.Access(Accessor.Field);
            map.Table("OrderItems");
            map.Key(a => a.Column("OrderId"));
            map.Id(a => {
                a.Column("Id");
                a.Generator(Generators.Identity);
            });
        }, relation => relation.Component(map => {
            map.Access(Accessor.Field);
            map.Property(a => a.Number, a => a.Access(Accessor.Field));
            map.Property(a => a.Goods, a => a.Access(Accessor.Field));
            map.Property(a => a.UnitPrice, a => a.Access(Accessor.Field));
        }));
    }
}

在 nhibernate 中有 IdBag 可以帮助我完成我需要的事情。请注意,无需为 orderItem class 创建 class 映射。 nhibernate 自动插入它。 但是您必须手动创建数据库。 希望对你有帮助。