views:

64

answers:

2

Hello, I am just learning NHibernate. I have been using examples from the documentation and here at stackoverflow, but I must be missing something.

I have a PARENT object that has a collection of CHILD. A CHILD is meaningless without a PARENT, so the database has FK set to NOT NULL. Implementing NHibernate from CHILD to PARENT works fine, though I have no need for this direction of relationship.

Instead, I tried to implement the PARENT owning the relationship, but I consistently get a Database error bubbling up "Cannot insert the value NULL into PARENT_ID". Neither the ID nor the entity of the PARENT is being stored in the CHILD when saving.

See the code sample below. Please advise.

Class Files

public class PARENT {
   private readonly IList<CHILD> _children = new List<CHILD>();
   public virtual Id { get; set; }
   public virtual void AddChild(CHILD child) {
      _children.add(child);
   }
}
public class CHILD {
   public virtual Id { get; set; }
}

Mappings

<class name="PARENT" table="Parent">
  <cache usage="read-write"/>

  <id name="Id" column="Id" unsaved-value="0" >
    <generator class="identity" />
  </id>

  <bag name="Children" access="field.camelcase-underscore" cascade="all-delete-orphan">
    <key column="ParentId"/>
    <one-to-many class="CHILD"/>
  </bag>

</class>
<class name="CHILD" table="Child">
  <cache usage="read-write"/>

  <id name="Id" column="Id" unsaved-value="0" >
    <generator class="identity"/>
  </id>

</class>
+2  A: 

You must define both sides of the relationship in your mapping and object model. You then declare one as inverse="true" in the mapping. So something like this should work:

Class files

public class PARENT {
   private readonly IList<CHILD> _children = new List<CHILD>();
   public virtual Id { get; set; }
   public virtual void AddChild(CHILD child) {
      _children.add(child);
   }
}
public class CHILD {
   public virtual PARENT Parent { get; set; }
   public virtual Id { get; set; }
}

Mappings

<class name="PARENT" table="Parent">
  <cache usage="read-write"/>

  <id name="Id" column="Id" unsaved-value="0" >
    <generator class="identity" />
  </id>

  <bag name="Children" access="field.camelcase-underscore"
     cascade="all-delete-orphan" inverse="true">
    <key column="ParentId"/>
    <one-to-many class="CHILD"/>
  </bag>

</class>
<class name="CHILD" table="Child">
  <cache usage="read-write"/>

  <id name="Id" column="Id" unsaved-value="0" >
    <generator class="identity"/>
  </id>
  <many-to-one name="Parent" class="PARENT" column="ParentId" />

</class>

And you might want to change your AddChild method to this:

public virtual void AddChild(CHILD child) {
   _children.add(child);
   child.Parent = this;
}
David M
Thanks for responding! for the CHILD mapping, "class" is not a valid attribute for "property". I tried removing it, but then it cannot determine Type for PARENT. I also tried changing "property" to "many-to-one", but that resulted back to my earlier error from trying to insert a null foreign key.
Todd Gardner
Sorry, was off the top of my head. Will edit post.
David M
That works, but I still have "Cannot insert the value NULL into PARENT_ID". My steps are, create PARENT, persist PARENT, flush, Add Child to PARENT, persist PARENT, Error, Cry, try Stackoverflow.
Todd Gardner
Are you setting the Parent property on the Child, as I suggested in my replacement AddChild method in my edit?
David M
You've changed AddChild method to set reference to the parent?
epitka
Excellent, adding "child.Parent = this;" worked.. Is this expected behavior? I had expected NHibernate to handle this for me.
Todd Gardner
@epitka - yes, exactly that. So AddChild sets both directions of the relationship. @Todd - the mapping instructs NHibernate to look at the child->parent relationship for persistence purposes. You are in fact only adding to the child collection for the benefit of your own code. You still need to set the relationship in the opposite direction. At the point where NHibernate gets involved, all it does is look at this child->parent relationship - and until you try to persist, NHibernate doesn't get in the way at all!
David M
A: 

I had the same problem, for now I just removed the not-null constraints on this kind of foreign keys. Since the database is only used with NH, it is not possible to get any null values there.

I think NHibernate stores the child first. Then it stores the parent. Because you need identity ids, it does not have a primary key for the parent before it is inserted to the database. Then it needs to update the child afterwards.

Try another id generator. hilo is recommended. This is also faster.

Stefan Steinegger
Unfortunately, the database is utilized from a couple different clients, so I prefer to maintain this integrity in the database directly.
Todd Gardner