如何映射一对多 nhibernate 类映射



这是我的订单和订单项目类:

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

在映射此代码时,我希望订单位于订单表中,而订单项存储在名为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 个订单项目的订单时,它会插入订单而不是订单项目 感谢您的帮助

您已将父Order映射为Itemsbagmapper.Inverse(true);,该告诉NHibernate您不希望父级映射此关系。

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


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

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


在 NHibernate 中,需要在自由class布局和最佳数据库性能之间进行权衡(尽管在这种情况下非常小)。

如果您真的不希望OrderItemOrder属性链接回其父属性,那么每当OrderItem的父Order发生变化时,您都会收到对 SQL 的额外 UPDATE 调用,如果创建OrderItems少于您使用它们执行的操作的 ~10%,则此成本实际上可以忽略不计。

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

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

你可以通过使用OrderId属性而不是对Order的完整引用来解决这个问题,但你必须查找它。


至于将它们保存到数据库中,最简单的方法是将mapper.Cascade(Cascade.None);更改为mapper.Cascade(Cascade.AllDeleteOrphan);(可能不是确切的类名)。这将确保每当您在Order上修改该集合时,Save()/Update()OrderItems集合中的所有OrderItem都将相应地在数据库中更新。

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


最后查看 Nhibernate 文档中的bagvsset,我怀疑你想在这里set,我只会inverse(true)一起使用bag。如果您使用inverse(false)bag则会出现性能损失,除非可以识别项目(您的OrderItemId,所以可以!),但如果可以识别项目,那么为什么不让它成为set

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

谢谢你@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会自动插入它。 但是您必须手动创建数据库。 我希望这对你有所帮助。

相关内容

  • 没有找到相关文章

最新更新