Saturday, January 26, 2008

Nhibernate many-to-one (M:1) mapping

While working on a project recently, I started to analyze all the query generated by the Nhibernate for the different mapping available. Playing with the fetching strategy and lazy loading option, I will be able to see how the different queries are form. Interestingly, I realized that the M:1 mapping does not have a lazy loading option, and decided to further investigate more on this mapping.

M:1 mapping is use for object reference, the same idea as the M:1 association in the relational model. To test and see the query generated by the Nhibernate, I decided to load a class with M:1 mapping. To my surprise, the generated query pulled about 80% of the tables found in my database. Going back to the class, the class that I am trying to load have two many-to-one mapping and the referenced class also have other M:1 mapping within the class itself and the reference traverse for about four times.

With this, I started to reflect if there are something wrong with my class design and also wondering why the M:1 mapping does not support the lazy loading option? Not too sure but probably the idea of the many-to-one mapping reference object in terms of records in the database will always be smaller than the number of records for the referencing object. But imagine if the targeted growth for the referenced object is to peak to about a million records in the database and the referencing objects is about 10 million. The penalty of loading both the referencing and referenced object will be pretty obvious without the lazy loading option.

Apart from the lazy loading option, the default fetching strategy seems to be different from what is stated in the Nhibernate document for the M:1 mapping. Without specifying any fetching strategy for the M:1 mapping, the fetching strategy use is "Join". Only when it is explicitly specific to "Select", the select strategy is use. Therefore, I urged anyone using the Nhibernate with certain performance consideration in mind should start looking at the query generated by Nhibernate.

To workaround this issue, for all M:1 mapping without much targeted growth in records, the mapping will be maintain as the performance might be minimal. For mapping with a high targeted growth and with the referenced object information seldom used, the mapping relationship should be removed and only loaded when necessary through the referenced object. However, that means the logic of what to load have to be manually managed and it will be similar to the N+1 problem found with lazy loading. Ayende Rahien have some good explanation and the workaround for this N+1 problem.