Add/delete item to bag collection
I am working with nHibernate, and trying make sense of bag collections. My data structure is relatively straight-forward...
Entry:
<class name="Entry">
<id name="id" column="EntryId">
<generator type="guid.comb"/>
</id>
<property name="Name" column="Name"/>
<bag name="Results" table="Results" cascade="all">
<key column="EntryId" />
<one-to-many class="Result"/>
</bag>
</class>
Result:
<class name="Result">
<id name="id" column="ResultId">
<generator type="guid.comb"/>
</id>
<property name="Score" column="Score" />
<many-to-one name="Entry" class="Entry" cascade="all" />
</class>
What I would like to do, which doesn't seem to be working, is the following:
Entry entry = new Entry();
entry.Name = "Name";
// have tried saving at this point to:
// dbSession.SaveOrUpdate(entry);
Result result = new Result();
result.Score = 100;
entry.Results.Add(开发者_如何学编程result);
dbSession.SaveOrUpdate(entry);
It seems to be creating the entry record in the database, but not the result record. In my database, I have EntryId as a foreign key in the Result table. Similarly, I would like to be able to remove the result object from the collection, and have it persist to the database. I thought the cascade feature took care of this, but not sure what I have done wrong...
EDIT
I now have it adding the result object into the database, but delete does not seem to work:
Entry entry = Entry.Load(id);
entry.Results.Remove(result);
dbSession.SaveOrUpdate(entry);
I have tried adding cascade="all-delete-orphan", but this seems to remove both parent and children. I just want it to delete the one entry object from the database??
In the end, this came down to my hbm file mappings not being correct.
Entry.hbm.xml
<bag name="Results" table="Result" lazy="false" inverse="true" cascade="all-delete-orphan">
<key column="EntryId"/>
<one-to-many class="Result"/>
</bag>
Result.hbm.xml
<many-to-one name="Entry" class="Entry" column="EntryId"/>
I originally had cascade="all-delete-orphan" on the many-to-one mapping, which was not correct. What happened was that all children and the parent record was being deleted.
I can now add and remove with the following:
Result r = new Result();
Entry entry = new Entry();
// AddResult method sets the Entry object of the Result
// result.Entry = this;
entry.AddResult(r);
session.SaveOrUpdate(entry);
To delete:
entry.Results.Remove(result);
session.SaveOrUpdate(entry);
To add to a collection you need to explicitly save the child object when it is added. Ditto when you delete an object from a collection.
So you would do:
entry.Results.Add(result);
session.Save(result);
session.Save(entry);
session.Flush();
The foreign key also has to be nullable. The reason why you have to do this is NHibernate has to save the child first with no association to the parent. Then when the parent is saved the foreign key column on the child gets updated with the parent's Id, creating the relation. This is because NHibernate may not have the needed parent id key value until the second operation (parent is saved) has completed.
I guess you have this part figured out.
Delete works the same way for different reasons - remove the child from the parent collection, then delete the child explicitly, then update the parent:
entry.Results.Remove(result);
session.Delete(result);
session.Update(entry);
session.Flush();
You removed result from the collection and updated the entry. That only tells Nhibernate to delete the relationship between the entry and the result - you never actually deleted the result object itself.
I notice that, in your collection, you have defined the FK column as:
<key column="EntryId" />
But you are not overriding the column
in your many-to-one
, which means you have two different columns (Entry and EntryId) for the same relationship.
This might be it or not... but it doesn't hurt to check :-)
if you are using Mapping by Code then use both Cascade.All and Cascade.DeleteOrphans options. unlike the xml mapping, there is no single option for "all-delete-orphan" in Mapping by Code.
Bag(x => x.Results, c =>
{
c.Key(k =>
{
k.Column("EntryId");
});
c.Cascade(Cascade.All | Cascade.DeleteOrphans);
}, r => r.OneToMany())
精彩评论