-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: one-to-many usage
PostPosted: Fri Jan 25, 2008 2:35 pm 
Newbie

Joined: Fri Jan 25, 2008 2:08 pm
Posts: 2
Hi all,

I am new to NHibernate and currently trying to persuade our architect to use NHibernate to replace our terrible in-house developed DAL.

However, I have got a problems in the little sample I created for testing one-to-many.

Firstly, here's the .hbm.xml

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Entities" namespace="Entities">
  <class name="Parent" table="parents">

    <!-- primary key -->
    <id name="ParentID" column="parentid" type="Int32">
      <generator class="native"/>
    </id>
   
    <!-- properties -->
    <property name="ParentProperty1" column="parentproperty1" />
    <property name="ParentProperty2" column="parentproperty2" />

    <bag name="Children" table="children"  inverse="true" cascade="all-delete-orphan">
      <key column="parentid"/>
      <one-to-many class="Child"/>
    </bag>
  </class>

  <class name="Child" table="children">
    <!-- primary key -->
    <id name="ChildID" column="childid" type="Int32">
      <generator class="native"/>
    </id>
   
    <!-- properties -->
    <property name="ChildProperty1" column="childproperty1" />
    <property name="ChildProperty2" column="childproperty2" />
  </class>
</hibernate-mapping>


Now the entity classes

Code:
namespace Entities
{
    [Serializable]
    public class Child
    {
        private int childID;
        private string childProperty1;
        private string childProperty2;

        public virtual int ChildID
        {
            get { return childID; }
            set { childID = value; }
        }

        public virtual string ChildProperty1
        {
            get { return childProperty1; }
            set { childProperty1 = value; }
        }

        public virtual string ChildProperty2
        {
            get { return childProperty2; }
            set { childProperty2 = value; }
        }
    }

    [Serializable]
    public class Parent
    {
        private int parentID;
        private string parentProperty1;
        private string parentProperty2;
        private IList children;

        public virtual int ParentID
        {
            get { return parentID; }
            set { parentID = value; }
        }

        public virtual string ParentProperty1
        {
            get { return parentProperty1; }
            set { parentProperty1 = value; }
        }

        public virtual string ParentProperty2
        {
            get { return parentProperty2; }
            set { parentProperty2 = value; }
        }

        public virtual IList Children
        {
            get { return children; }
            set { children = value; }
        }
    }
}


Now when I ran following lines of code, the parent got saved to database successfully but for some reason child1 is not saved to database (i.e. the children table is empty).

Code:
Parent parent = new Parent();
parent.ParentProperty1 = "pp1";
parent.ParentProperty2 = "pp2";
parent.Children = new ArrayList();
session.Save(parent);

Child child1 = new Child();
child1.ChildProperty1 = "cp1";
child1.ChildProperty2 = "cp2";
parent.Children.Add(child1);
session.SaveOrUpdate(parent);


I tried to remove the 'session.Save(parent);', so now the client code looks like this:

Code:
Parent parent = new Parent();
parent.ParentProperty1 = "pp1";
parent.ParentProperty2 = "pp2";
parent.Children = new ArrayList();

Child child1 = new Child();
child1.ChildProperty1 = "cp1";
child1.ChildProperty2 = "cp2";
parent.Children.Add(child1);
session.SaveOrUpdate(parent);


Now both the 'parent' and 'child1' got saved to database successfully, however, the 'parentid' column of 'child1' is NULL.

To summarise my question, can anyone tell me why:
1. Saving the parent first then add children and save again doesn't save the children properly.
2. how come the 'parentid' of children table is always null even though I have added them to an Parent object.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 25, 2008 2:41 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
Take a look at the Cat/kittens example in the help file.

Here's a quick sketch of what you should do:

Parent parent = new Parent();
parent.ParentProperty1 = "pp1";
parent.ParentProperty2 = "pp2";
parent.Children = new ArrayList();

Child child1 = new Child();
child1.ChildProperty1 = "cp1";
child1.ChildProperty2 = "cp2";
child1.Parent = parent;
parent.Children.Add(child1);

session.SaveOrUpdate(parent);

You don't need to explicitly save the parent first. The problem is just that adding a child to the collection isn't enough for nhibernate to know about the association. You need to explicitly define the association between the two.

In your mapping for your child, you may want to define Parent this way:

<many-to-one name="Parent" column="parentId" not-null="true" cascade="none" />

Sorry, a little muddled of an example I suppose. I can provide a better one if it will help. The cat/kitten example in the help is really useful though.

Another thing to consider... a great starting point for moving to NHibernate might be to at least use NHibernate to manage your database connections. I'm doing this for a project that I'm in the middle of migrating back to NHibernate... I let it manage the connections that I then use just to execute my stored procedures.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 25, 2008 2:52 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
Here's a slightly better example:

Service.hbm.xml
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
   <class name="Sigar.Muddi.Service, Sigar" table="dbo.muddi_Services">
      <id name="Id" type="Int32" access="nosetter.camelcase">
         <column name="ServiceId" not-null="true" unique="true"/>
         <generator class="native" />
      </id>
      
      <property name="Name" column="Name" />

      <bag name="ProjectServices" table="dbo.muddi_Service_Projects" lazy="true" generic="true" inverse="true" cascade="all-delete-orphan">
         <key column="ServiceId" />
         <one-to-many class="Sigar.Muddi.ProjectService, Sigar" />
      </bag>
   </class>
</hibernate-mapping>


ProjectService.hbm.xml
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
   <class name="AllianzLife.Sig.Sigar.Muddi.ProjectService, AllianzLife.Sig.Sigar" table="dbo.muddi_Service_Projects">
      <id name="Id" type="Int32" access="nosetter.camelcase">
         <column name="ServiceProjectId" not-null="true" unique="true"/>
         <generator class="native" />
      </id>

      <property name="Notes" column="Notes" />

      <many-to-one name="Project" column="ProjectId" not-null="true" cascade="none" />
      <many-to-one name="Service" column="ServiceId" not-null="true" cascade="none" />
   </class>
</hibernate-mapping>


Service.cs
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace Sigar.Muddi
{
   [Serializable]
   public class Service
   {
      private int id;
      private string name;

      private IList<ProjectService> projectServices;

      public int Id
      {
         get { return id; }
         set { id = value; }
      }

      public string Name
      {
         get { return name; }
         set { name = value; }
      }

      public virtual IList<ProjectService> ProjectServices
      {
         get
         {
            if (projectServices == null)
               projectServices = new List<ProjectService>();

            return projectServices;
         }
         set
         {
            projectServices = value;
         }
      }

      public void AddProjectService(ProjectService projectService)
      {
         projectService.Service = this;
         ProjectServices.Add(projectService);
      }
      
      public void Save()
      {
         ServiceDataAccess.Save(this);
      }

      public void Delete()
      {
         ServiceDataAccess.Delete(this);
      }
   }
}


ProjectService.cs

Code:
namespace Sigar.Muddi
{
   [Serializable]
   public class ProjectService
   {
      private int id;
      private string notes;

      private Project project;
      private Service service;

      public int Id
      {
         get { return id; }
         set { id = value; }
      }

      public string Notes
      {
         get { return notes; }
         set { notes = value; }
      }

      public Project Project
      {
         get { return project; }
         set { project = value; }
      }

      public Service Service
      {
         get { return service; }
         set { service = value; }
      }
   }
}


I can then create a new Service, add several ProjectService instances to it, and then save Service. It will cascade down. It will also cascade down on deletes.

Note, I set the relationship by passing a ProjectService instance to service.AddProjectService. This method sets the parent for my ProjectService instance.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 25, 2008 3:44 pm 
Newbie

Joined: Fri Jan 25, 2008 2:08 pm
Posts: 2
Cheers man, thank you very much for the detailed answer


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.