如何映射一对多 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 您不希望父级 Order
在 Items
发生变化时处理对 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 文档中的 bag
与 set
,我怀疑你在这里想要一个 set
,我只会使用 bag
和 inverse(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 自动插入它。
但是您必须手动创建数据库。
希望对你有帮助。
这是我的订单和 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 您不希望父级 Order
在 Items
发生变化时处理对 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 文档中的 bag
与 set
,我怀疑你在这里想要一个 set
,我只会使用 bag
和 inverse(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 自动插入它。 但是您必须手动创建数据库。 希望对你有帮助。