如何映射关系,其中子端点通过Id属性公开,而不是通过整个Parent对象公开?
以下是示例:
class Parent {
public Guid Id { get; set; }
public List<Child> Chlidren { get; set; }
}
class Child {
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
以下是我正在使用的等效映射:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Blabla"
namespace="Blabla"
auto-import="false">
<typedef name="ChildrenList" class="Blabla" />
<class name="Parent" table="Parent" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
</class>
<class name="Child" table="Child" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<!-- How to map ParentID here? -->
</class>
</hibernate-mapping>
当我创建父项时,将一些子项添加到Children
集合中,然后保存父项,一切都很好。但是如果首先保存父对象,那么创建一个子对象,将其ParentID属性设置为父对象的ID,然后我得到
NHibernate.PropertyValueException:
not-null property references a null or transient value Child._Parent.ChildrenBackref
在创建NHibernate配置时,所有映射many-to-one
关系的尝试都会导致不同的异常。主要是关于对象类型不匹配。
我确信NHibernate能够处理这种情况。我必须要有一些相当基本的东西。
修改
我认为对示例测试有意义,因为上述例外而失败:
var child = new Child(Create.Saved<Parent>().Id); // this sets the ParentId property
this.Repository.Save(child); // here I get the exception
我的想法为什么NHibernate提出这个问题:Children
类Parent
属性的映射方式表明如果没有父级(<key column="ParentID" not-null="true" />
),孩子就不能存在。当我试图坚持一个孩子时,NHibernate试图解决这种关系(找到这个孩子所关联的父亲)并失败,因为在映射中没有给出子端点(否则将是ParentId
属性),检查自己的Child._Parent.ChildrenBackref
端点,无论它是什么。
这看起来像是一个理想的解决方案:将ParentId
属性映射为关系的子端点。这将强制NHibernate通过使用ParentId
属性值作为父级主键来解析父级。
事情是我不知道是否可能。
答案 0 :(得分:1)
你在NHibernate中拥有的一对多/多对一关系总是需要占主导地位(即管理“保存”的一方)。
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
以上是一对多关系,其中优势一方是父母。这意味着,您保存父...并且将首先保存父项,然后保存子项(ParentId为null),然后将发出后续更新以设置child.ParentId。
注意:
首先使用ParentId = null插入子项...如果您有一个db或映射限制,表示ParentId不能为null,则此操作将失败。
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false"
inverse=true>
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
请注意 inverse = true 属性。这意味着子对象在关系中占主导地位,这意味着子对象负责。将插入父级,然后将Id分配给child.ParentId,然后将子项插入并且已设置ParentId 。
在许多情况下,当然,你想要采取任何一种方式。最简单的方法是管理两端的关系(不幸的是,你必须自己做)。
在Parent上,你有一个方法:
public void AddChild(Child child)
{
Children.Add(child);
child.ParentId = Id;
}
public void RemoveChild(Child child)
{
Children.Remove(child);
child.ParentId = null;
}
在孩子身上,你有一个方法:
public void SetParent(Parent parent)
{
ParentId = parent.Id;
parent.Children.Add(this);
}
使用这些方法添加/删除/设置,执行操作后双方都是一致的。那么,无论你是否在包上设置 inverse = true 都无关紧要。
请参阅http://www.nhforge.org/doc/nh/en/index.html#collections-example