views:

246

answers:

3

Hello

Nhibernate users, professionals, gurus and developers are expected. Please help !!!

I want to realise a n:m relation between two classes. A student attends in more courses and a course consists of more students as members. I do a bidirectional association many-to-many with bag to get the both lists from each site.

The two Student and Course classes:

public class Student {         
     // Attributes........

     [XmlIgnore]
     public virtual IList MyCourses { get; set; }

     // Add Method
     public virtual void AddCourse(Course c)
    {
        if (MyCourses == null)
            MyCourses = new List<Course>();

        if (!MyCourses.Contains(c))
            MyCourses.Add(c);

        if (c.Members== null)
            c.Members= new List<Student>();

        if (!c.Members.Contains(this))
            c.Members.Add(this);
    }

    public virtual void RemoveCourse(Course c)
    {
        if (MyCourses != null)
            MyCourses.Remove(c);

        if (c.Members!= null)
            c.Members.Remove(this);
    }
}

public class Course {         
     // Attributes........

     [XmlIgnore]
     public virtual IList Members { get; set; }
}

In database there are two tables t_Student, t_Course and a relation table tr_StudentCourse(id, student_id, course_id).

<class name="Student" table="t_Student" polymorphism="explicit">
 ..... 
 <bag name="MyCourses" table="tr_StudentCourse">
   <key column="student_id" />
   <many-to-many class="Course" column="course_id" not-found="ignore" />
 </bag>    
</class>

<class name="Course" table="t_Course" polymorphism="explicit">
 ..... 
 <bag name="Members" table="tr_StudentCourse" inverse="true">
   <key column="course_id" />
   <many-to-many class="Student" column="student_id" not-found="ignore" />
 </bag>    
</class>

Course was chosen as inverse in the bidirectional association. I did the same as example (Categorie, Item) in section 6.8 of nhibernate documentation. So I saved the student object after inserting a course in the list MyCourses by calling the Add/Remove-method.

Student st1 = new Student();
Course c1 = new Course();    
Course c2 = new Course();
st1.AddCourse(c1);
st1.AddCourse(c2);
session.saveOrUpdate(st1);

That works fine, the st1, c1 and their relation (st1,c1) can be find in the database. The relation datasets are (id=1, st1.id, c1.id) and (id=2, st1.id, c2.id). Then I add more courses to the object st1.

Course c3 = new Course();
st1.AddCourse(c3);
session.saveOrUpdate(st1);

I can see the 3 relation datasets, but the two old relations were deleted and new three were created with another new id. (id=3, st1.id, c1.id), (id=4, st1.id, c2.id) and (id=5, st1.id, c3.id). There are not dataset with id=1 and 2 more in relation table.

The same by deleting if I remove a course from student.MyCourse and then save the student object. All collection was also deleted and recreated a new list which less one deleted element. That problem makes the id in the relation table increates very fast and a have troble by doing a backup of relation.

I have looked some days in internet, documentation and forums to find out why the whole old collection was deleted and a new as created by each changing, but I was not successful. It is a bug from nhibernate mapping or did I do any wrong?

I am very very grateful to your help and answer.

Nhibernate documentation http://nhforge.org/doc/nh/en/index.htm

+2  A: 

NHibernate can't create, delete or update rows individually, because there is no key that may be used to identify an individual row.

By note from "6.2. Mapping a Collection"

As soon as you have id in tr_StudentCourse you can try using indexed collections, i.e. replace <bag> with <map> or similar and add <index> element to the mapping:

<index
    column="id"
    type="int"
/>

or even create a special entity for the relation table to use with <index-many-to-many>.

Li0liQ
A: 

This is what I've found on the NHibernate website:

Hibernate is deleting my entire collection and recreating it instead of updating the table.

This generally happens when NHibernate can't figure out which items changed in the collection. Common causes are:

  • replacing a persistent collection entirely with a new collection instance passing NHibernate a manually constructed object and calling Update on it.

  • serializing/deserializing a persistent collection apparently also causes this problem.

    • updating a with inverse="false" - in this case, NHibernate can't construct SQL to update an individual collection item.

Thus, to avoid the problem:

pass the same collection instance that you got from NHibernate back to it (not necessarily in the same session), try using some other collection instead of <bag> (<idbag> or <set>), or try using inverse="true" attribute for <bag>.

Frederik Gheysels
A: 

As you recommended I use the <set> collection instead of <bag> on the student class site and it works fine now.

Thank you guys very much for the answer.

newbieNhibernate