如何映射一对多nhibernate类映射

时间:2018-07-26 08:25:49

标签: c# nhibernate

这是我的order和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表和orderItem中,以存储在称为OrderItems的差异表中。 这是我的映射:

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

    } 
}

我也在数据库中创建了表,但是当我插入带有3 orderItems的order时,它会插入order但不会插入orderitems 谢谢您的帮助

2 个答案:

答案 0 :(得分:1)

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

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


您还设置了mapper.Cascade(Cascade.None);,它告诉NHibernate当OrderItems中更改时,您不希望父Session处理Save()上的任何操作。

因此,除非您在每个Item上显式调用class,否则它们将永远不会保存。


NHibernate在自由OrderItem布局和最佳数据库性能之间进行权衡(尽管在这种情况下很小)。

如果您确实不希望Order拥有一个链接回其父级的Order属性,那么只要父级OrderItem为{ OrderItems的更改,如果inverse(false)的创建少于您对其执行的操作的〜10%,则在实践中此成本可以忽略不计。

在这种情况下,您可以将OrderMapping上的Items设置为OrderItem

但是我的选择是给Order一个OrderItemMapping字段或属性(您可以使用NHibernate映射一个私有字段!),然后将inverse(false)的映射返回给父对象与OrderItem一起使用,这样在保存孩子时,他们将处理该关系。保存之前,您必须确保每个Order都填写了OrderId字段/属性!

您也许可以通过使用Order属性而不是对mapper.Cascade(Cascade.None);的完整引用来实现此目的,但是您必须对此进行查找。


关于将它们保存到数据库,最简单的方法是将mapper.Cascade(Cascade.AllDeleteOrphan);更改为Order(可能不是确切的类名)。这样可以确保无论何时在Save()上修改该集合,然后在Update()Order / OrderItem上修改{{1}中的所有Items }集合将相应地在数据库中更新。

您还可以检出不太严格的Cascade或按照当前设置的要求手动保存。


最后检查Nhibernate文档中的bagset,我怀疑您想在这里使用set,我只会在bag中使用inverse(true) 。如果您使用inverse(false) bag,则性能会受到影响,除非可以识别这些项目(您的OrderItem具有Id,所以可以!),但是如果可以识别项目,然后为什么不将其设置为set

您拥有inverse(false) bag带有可识别项目的唯一原因是,如果这些标识符与数据库中的主键不相关,那么这种情况就不会经常出现(或者就我而言!)。

答案 1 :(得分:1)

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

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类创建类映射。 nhibernate会自动将其插入。 但是您必须手动创建数据库。 希望对您有帮助。