Reputation: 551
I have database structure with invoice
and invoiceParents
with one-to-many
, inverse
equals "false"
and cascade
equals "all-delete-orphan"
. Below some snippet from .hbm
for parent and children.
Parent:
<id name="InvoiceId" column="INVOICEKEYID" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SOME_SEQ</param>
</generator>
</id>
...
<bag name="Partners" table="PARTNER" cascade="all-delete-orphan" inverse="false" lazy="true">
<key column="INVOICEKEYID"/>
<one-to-many class="Partner"/>
</bag>
Children:
<id name="Id" column="PARTNERKEYID" unsaved-value="0">
<generator class="sequence">
<param name="sequence">ANOTHER_SEQ</param>
</generator>
</id>
...
<many-to-one name="Invoice" column="INVOICEKEYID" class="Invoice" />
When I make first saveOrUpdate(invoice)
, there are INSERT statements, for invoice and invoicePartners, with empty fk and after it UPDATE statements, for invoicePartners, with proper fk. Till this time everything works good.
When I want to update there are only INSERT for invoicePartners, one UPDATE for invoice. There are no UPDATE or DELETE AND INSERT for invoicePartners.
I watch it in vs output.
How does nHibernate work? Firstly I should remove partners and then insert new ones or nHibernate will update it automatically?
Upvotes: 0
Views: 381
Reputation: 1091
This all stems from the owning bag
(inverse="false"
) that you have created. As a general rule, never create an owning bag, a bag
with (inverse="true"
) provides the ultimate performance, since operations cannot fail.
In this case, I would be careful that you don't end up with a PK violation, NHibernate will not handle the case where you insert a PK violating invoicePartner
, because you have told it that it is a bag
and not a set
. If you don't have a PK and want duplicate invoicePartner
s then continue with a bag
, if you don't, switch to set
. Obviously if your class
ensures this internally then you will avoid this problem.
Be careful with bag
generally though, I think you are fine in this case since the mapped entities can be identified, so you should get similar performance to a set
. But if they cannot be identified, then NHibernate will have to delete all of the children and re-insert them all on any collection change!
On addition of an invoicePartner
to an invoice
:
The invoicePartner
is not going to save the relation, since you have told the mapping that you want the invoice
to own it.
Therefore, on any insert or update to the DB, the invoicePartner
cannot fill in the INVOICEKEYID
column.
So the parent invoice
waits until the invoicePartner
exists in the DB, then updates the foreign key column, as you observed.
Depending on the default behaviour, this update may be a complete waste of time, since it looks to me that the child would already be saving this foreign key itself!
On update of the invoicePartner
collection:
What you've said doesn't really fit. NHibernate will not call update for invoice
if all you do is change the invoiceParnter
collection. There is nothing to update on the invoice
table!
The cascade="all-delete-orphan"
will ensure that the parent keeps the collection synced with itself, it will insert, update and delete rows from the invoicePartner
table accordingly as long as you save or update the parent after changing the collection!
But you should set inverse="true"
on the parent bag
and add inverse="false"
to the invoicePartner
many-to-one
map. After doing this, check for the case I mentioned in the 3rd paragraph, where NHibernate cannot identify the entities, and make sure that it is indeed associating invoicePartners
by their identifier and making the changes efficiently.
Ref: http://nhibernate.info/doc/nhibernate-reference/example-parentchild.html
Upvotes: 1