views:

327

answers:

3

After having changed around my mappings a bit ( see my other question about cascade-delete for reasons) , i tried inserting a completely new object and all its subclasses.

After this another problem emerged, an issue with the insertion of the keys into the database. Situation is as follows:

I have an object with 2 tiers of subclasses, both a collection.

lets call the object Parent, this one has a collection of childs, and every enitity in this collection has its own collection of enitities. Which are mapped as follows.

Parent's mapping of its <set>

<!--Parent-->
<set name="Collection"
     table="Table"
     cascade="all-delete-orphan"
     batch-size="15"
     inverse="true">
  <key column="ParentID"/>
  <one-to-many class="CollectionObject,CollectionObject-ns"/>
</set

CollectionObject's Mapping

<id name="ID" column="ID">
  <generator class="native"/>
</id>

<!-- property mappings-->
<property name="ParentID" column="ParentID" not-null="true"/>

<!--collection mapping-->
<set name="Collection"
     table="Table"
     cascade="all-delete-orphan"
     inverse="true"
     batch-size="15">
  <key column="ChildID"/>
  <one-to-many class="CollectionObject,CollectionObject-ns"/>
</set>

The mapping mapping of the object in the 2nd collection is similar tot he above. The envisioned scenario would be, I save Parent, this would trigger the save of subclasses/collections.

e.g. I have Parent with ID = 1, Parent is filled with 1 collection, and this collection has 1 collection of its own.

So I save Parent, parent gets an ID from database (native sql identity). Now the first collection should get its ParentID property filled with the ID the Parent just got from the database. And the collection of the collection should get its ChildID filled with the ID the Child got from the database in the same way as Parent got his ID.

What happens now is (I checked created SQL in NHProf) Everything gets inserted, with its own ID, But the collections do not get their keycolumn filled with the ID of their parentclass. (instead it just gets 0 inserted)

So my question boils down to, what did i forget to add to my mapping which causes the to happen? Does the key column not do what i think it should do?

If i forget to add anything here, please say so. I will gladly give additional information.

update

I think the problem might have to do with the childs not having a <many-to-one> tag. So i tried to add one of those to the first child mapping. I came up with this

   <many-to-one name="ParentID"
             class="Parent,Parent-ns"
             column="ParentID"
             not-null="true"/>

However, this setup gives me the following error. Exception occurred getter of Parent.ParentID

with InnerException {"Object does not match target type."}

Sadly, This error does not give me any ideas of where to continue.

A: 

I think it would be helpful if you could show us your classes as well. Have you checked that your Parent class really has a ParentID property of the type Parent, also I assume you have now removed the

<property name="ParentID" column="ParentID" not-null="true"/> 

from your mapping and replaced it with the many-to-one? Finally if you want your child objects to have collections of child objects then the child object will also need a many-to-one

<many-to-one name="CollectionObjectParent"
             class="CollectionObject,CollectionObject-ns"
             column="ChildID"
             not-null="true"/>
Gareth
Hey Gareth,Im not sure if I understand you completely. Parent should have a property named ParentID of its own type?like Parent ParentID {get; set;}At the moment ParentID is just an integer, im confused now :)
marn
Rather than adding a reference to the DocumentID you need to reference the actual parent object Document. See the link for info on how to handle parent child situations - hope it helps http://www.nhforge.org/doc/nh/en/index.html#example-parentchild
Gareth
I've read trough the documentation close to 20x but I'm still missing something im affraid. I posted a workaround below, and it has to do for the moment, but still open for suggestions. Currently i'm watching the http://www.summerofnhibernate.com/ sessions. in the hope it get a little bit more clear for me.
marn
A: 

Gareth,

I did indeed remove the double property. But im getting the error (posted in first posts update) when i keep the <many-to-one>
Im totally out of ideas of what is causing this My classes and their mappings look like this: (sorry for the amount of text incoming)

Parent Object class (Document)

namespace BusinessObjects.Correspondentie
{
    public class Document : IDocument
    {
        public int DocumentID { get; set; }
        public IAcceptgiro Acceptgiro{ get; set; }
        public ICollection<IDocumentBodyregel> Bodyregels{ get; set; }
    }
}

Child Object class (DocumentBodyregel)

namespace BusinessObjects.Correspondentie
{
    public class DocumentBodyregel : IDocumentBodyregel
    {
        public int ID { get; set; }
        public int DocumentID { get; set; }
        public ICollection<IDocumentBodyregelWaarde> Waardes { get; set; }
    }  
}

And the last one (DocumentBodyregelWaarde)

namespace BusinessObjects.Correspondentie
{ 
    public class DocumentBodyregelWaarde : IDocumentBodyregelWaarde
    {
        public int ID { get; set; }
        public int DocumentID { get; set; }
        public int RegelID { get; set; }
    }
}

Document is mapped as follows:

<hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2">
<!-- mapping document-->
<class name="BusinessObjects.Correspondentie.Document, 
             BusinessObjects.Correspondentie" 
       proxy="Interfaces.Correspondentie.IDocument, 
              Interfaces.Correspondentie"
       table="Documenten">

<!--ID mapping-->
<id name="DocumentID" column="Doc_Id">
  <generator class="native"/>
</id>

<!--Acceptgiro-->
<many-to-one class="BusinessObjects.Correspondentie.Acceptgiro, 
                    BusinessObjects.Correspondentie"                  
             name="Acceptgiro" 
             column="Doc_Acceptgiro"
             cascade="all" 
             lazy="false"/>

<!--Bodyregels-->
<set name="Bodyregels"
     table="DocumentBodyRegels"
     cascade="all-delete-orphan"
     batch-size="15"
     inverse="true">
  <key column="DocumentID"/>
  <one-to-many class="BusinessObjects.Correspondentie.DocumentBodyregel, 
                      BusinessObjects.Correspondentie"/>
</set>  
</class>
</hibernate-mapping>

DocumentBodyregel is mapped like this:

<hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2">
<!-- mapping documentbodyregel-->
<class name="BusinessObjects.Correspondentie.DocumentBodyregel, 
           BusinessObjects.Correspondentie"         
     proxy="Interfaces.Correspondentie.IDocumentBodyregel, 
            Interfaces.Correspondentie"         
     table="DocumentBodyRegels">

 <id name="ID" column="ID">
  <generator class="native"/>
</id>

<!--<many-to-one name="DocumentID"
              class="BusinessObjects.Correspondentie.Document, 
                     BusinessObjects.Correspondentie"
              column="DocumentID"
              not-null="true"/>-->    

<property name="DocumentID" column="DocumentID" not-null="true"/>

<!--BodyregelWaarden-->
<set name="Waardes"
     table="DocumentBodyRegelWaarde"
     cascade="all-delete-orphan"
     inverse="true"
     batch-size="15">
  <key column="RegelID"/>
  <one-to-many class="BusinessObjects.Correspondentie.DocumentBodyregelWaarde, 
                      BusinessObjects.Correspondentie"/>
</set>
</class>
</hibernate-mapping>

and the last one like this

<hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2">
<!-- mapping documentbodyregelwaarde-->
<class name="BusinessObjects.Correspondentie.DocumentBodyregelWaarde, 
           BusinessObjects.Correspondentie"
     proxy="Interfaces.Correspondentie.IDocumentBodyregelWaarde,
            Interfaces.Correspondentie"            
     table="DocumentBodyRegelWaarde">

<id name="ID" column="ID">
  <generator class="native"/>
</id>

<!--<many-to-one name="RegelID"
              class="BusinessObjects.Correspondentie.DocumentBodyregel, 
                     BusinessObjects.Correspondentie"
              column="RegelID"
              not-null="true"/>-->

<!-- property mappings-->
<property name="RegelID" column="RegelID" not-null="true"/>
<property name="DocumentID" column="DocumentID"/>

</class>
</hibernate-mapping>
marn
A: 

Sadly, after playing around with mappings and reading trough half the internet, I have not been able to solve this.

The problem lies in the fact that i cant get to the scenario that <Document> gets his key from the database <generator class="native"/> and after this Inserts this key into the foreignkey properties of its underlying classes, inside just one session.Save() call.

e.g. for scenario i'd like session.Save(document)

-- * new document detected -- * get new identity

-- * document has subclass with a foreignkey to it!
-- * insert idenity into the specified foreignkey properties (guess here lies the problem)

and repeat this with all underlying classes.

I did however come up with a workaround (most ugly code ever, but it gets the job done) Im not really into sharing code this crappy, but it just might help people reading here.

I have not yet given up on solving this the right way, but for the moment this has to do. Stil open for a nudge in the better direction.

incoming crap code!

    public void InsertDocument(Document document)
    {
        using (ISession session = NHibernateHelper.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                 try
                 {                                          
                    IDocument document2 = new Document();
                    document2.Bodyregels = document.Bodyregels;
                    //for making the query a little neater
                    document.Bodyregels = null;                        

                    //lets get the elusive identity
                    session.Save(document);
                    session.Flush();

                    //reattach subclass and enter the id explicitly
                    document.Bodyregels = document2.Bodyregels;
                    foreach (DocumentBodyregel dbr in document.Bodyregels)
                    {
                        dbr.DocumentID = document.DocumentID;
                    }

                    //save it again, now with filled FK
                    session.Save(document);
                    session.Flush();

                    //now save the final subclass with FK's
                    foreach (DocumentBodyregel dbr in document.Bodyregels)
                    {
                        foreach (DocumentBodyregelWaarde dbrw in dbr.Waardes)
                        {
                            dbrw.RegelID = dbr.ID;
                            dbrw.DocumentID = document.DocumentID;
                        }
                    }

                    //and save the entire thing again (now with FK's)
                    session.Save(document);
                    transaction.Commit();
                }
                catch (Exception e)
                {
                    transaction.Rollback();
                    throw e;
                }
            }
        }
    }
marn