Thursday, February 7, 2008

NHibernate many to many collections

We had been having issues with entities not serialising across the wire (yes we are sending Nhibernate entities over WCF services) with not a lot of clues why some worked and some didn't.

Upon investingation it appeared it was the collections of some of these entiies that were causing the issues, not the enties themselves. I finally realised that we where creating entities for the join tables in the DB (which to me didnt really make sense), thus cause self referencing relationships in the object model, not a good place to be if this needs to be serialised to XML!

A bit of a clean up to the mapping files and we look to be all good.

I am sure this is the most trivial of trival ORM mapping stuff, but it was something that went un noticed around here for months, or at least swept under the rug by those that did.

Below is a the email outlining the scenario....

EMAIL TO WORK TEAM MATES

*If you use Nhibernate or other ORM’s this is of Note, otherwise feel free to delete*

ORM School time:

Background (trivial, but work with me here)

  • We have a whole bunch of Data Entities/ Database Tables that have “one to many” or “many to many” relationships
  • We use joining tables to create these relation ships I.e. to join AssetMetadata & GenreType we use AssetMetadataGenreType table.
  • We use an ORM (NHibernate) to convert the table structure to a usable C# managed code structure.
  • We also use the ORM to map these relationships.
  • Unfortunately we are mapping these tables too explicitly. There really is no such entity as an AssetMetadataGenreType, only an AssetMetadata entity and a GenreType entity. The AssetMetadataGenreType is only a relationship that has no context on its own.

This over eager mapping of joining table may be the problem of our serialisation issues.

Fore example: Currently we have a AssetMetadata entity that then has a collection of AssetMetadataGenreType with each one relating to a AssetMetadata and GenreType entity. The problem here is that every one of these causes a self referencing scenario (AssetMetadata è AssetMetadataGenreType è AssetMetadata). Normally no one would care. Serialisation however does not like this, as self referencing data and XML do not play nice.

Solution: Don’t map the joining tables.

These are not real entities so should not exist in the managed code world. They only exist in the DB thru necessity.

Step one: remove all of these mapping entities (well maybe not delete, but start re-factoring where possible)

Change the mapping so the subject entity knows about its relations. Inverse this relationship in the other mapping files IF NESSECARY. i.e. genre types do not need to know about there related asset metadata

Example below:

<!-- The links to associated genres --><!--

<bag name="AssetMetadataGenreTypes" lazy="true" inverse="true" cascade="all-delete-orphan">

<key column="ASSET_METADATA_ID" />

<one-to-many class="AssetMetadataGenreType" />

</bag>-->

<!-- RC: 07/02/2008: Trying a different approach so we dont try to pass self referencing data around,

I think this is a dormant issue waiting for collections to populate before it rears up and attacks us-->

<idbag name="GenreTypes" table="ASSET_METADATA_GENRE_TYPE" lazy="false">

<collection-id column="ID" type="Int32">

<generator class="sequence">

<param name="sequence">ASSET_METADATA_GENRE_TYPE_SEQ</param>

</generator>

</collection-id>

<key column="ASSET_METADATA_ID"/>

<many-to-many column="GENRE_TYPE_ID" class="GenreType" fetch="join"/>

</idbag>

Another benefit includes faster update times as we are taking advantage of the index of the primary key on the join table.

http://www.hibernate.org/hib_docs/nhibernate/html/collections.html#collections-idbag

I still have to test the hell out of this, however prelim test are all positive.

Rhys

1 comment:

RhysC said...

My views have changed on this. Please see: http://rhysc.blogspot.com/2009/06/many-to-many-joins-revisited.html