gorrch
gorrch

Reputation: 551

Updating children values in nHibernate

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

Answers (1)

starlight54
starlight54

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 invoicePartners 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

Related Questions