Tips on NHibernate Development

This is a follow-up article to the previous one on the same topic in Chinese.

Recent blogs are all expected to be in English since IME and language packs are not ready on the Ubuntu Linux that have newly been installed and I don't bother installing them until I've tried most of its exiting new features out; and also I do need to spend some time trying to think about some technical stuff more intensively in English.

And this article is supposed to be as short as the one that precedes it. It only puts down some very brief points regarding the usage of the ORM framework, so the potential reader should be familiar with the context of the problems and general terms involved in this technology.

Here below are the contents of the article in dot-point form with regards to specific design and implementation issues.

1. HasMany (one-to-many mapping) generally has to be defined Eager Loading or in some way at run time unproxied before the application can make full use of the underlying collections. Especially for custom collections that follow the design pattern shown below, (arrow indicating inheritance relationship; dot owner-property relationship; the diagram is for illustration purpose only, in real implementation, the actual name of these type may vary and the word 'collection' and 'col' would be replaced with a specific collection type like Set and List etc)

 PersistentGenericCollection <- CustomPersistentCollection

       .gcol; .col

both .gcol and .col defined in the base persistent collection class within NHbernate are instantiated at runtime by the framework through the corresponding collection factory into the same instance of a concrete functional collection type that is implemented with a completely business logic based consideration. However the problem is that if lazy loading is used for collections in one-to-many mappings, which is the default option, there's no guarantee that these objects are instantiated whenever the application is requesting them. And actually, as it seems to me unless using some other particular unproxy means, one has to explicitly invoke corresponding querying/loading methods on the working session to get them properly loaded, which is not desired in some situations where the design or project goal requires that the details of the collections and types should be hidden from the persistence logic itself. So it seems leaving the developer almost no choice but turn eager loading on (or equivalently lazy loading off) for collections of which types can not be determined from the persistence logic's perspective and lazy loading would cause an issue and for which more advanced runtime loading and unproxy techniques may not be used.

There are two articles regarding approaches handling proxy issues arising from lazyloading in two different ways, which are worth reading, as below (although so far it's not sure whether they are just proxies for individual entities or for collections as well, as from NHibernate point of view, they are remarkably different.)

[1] go without proxy, http://ayende.com/blog/4378/nhibernate-new-feature-no-proxy-associations

[2] identify the class of the instance the proxy is working with, http://stackoverflow.com/questions/2664245/identifying-nhibernate-proxy-classes

2. 'inverse' switch for HasMany() (one-to-many mapping)

This subtopic is also about one-to-many mapping, but unlike the one before which is largely related with loading, this mainly affects saving and has more to do with the mechanism for saving collection information for one-to-many mapping.

As far as ORM/database is concerned, collection data which is the central part of a one-to-many mapping is persisted by having a separate column in the table for the many-side entities that keeps the Id of the one-side entity. This column may back the property owned by the many-side object in a normal case, but may not in some other cases where it is not required or deliberately avoided by the design goal. However from the data persistence point of view, the column is always there and the mapping is always the same. To ensure this, whenever the back-referencing property is absent from the many-side entity, the 'inverse' switch has to be turned off to direct the framework to retrieve collection structure related data solely from collection in the one-side itself rather than from the unavailable back-reference as is with List type for which inverse is turned off to inform the framework of the fact that list order information can only be retrieved from the 'one' side. In fact, if the switch is left on, the saving process may go through but the back-referencing column will remain unset and the collection and the object relationship will fail to restore upon loading.

When putting together all the combination of conditions for one-to-many mappings concerning 'inverse' option, we may have the following table, (note that non-list collection might just refer to set/bag collections, as map or dictionary collection type is not actually a one-to-many mapping)

  List Non-list
back-reference available in object 'inverse' must be off with index specified and
thereby directed to the column in the many-side table
along with the reference
'inverse' could be turned on to enhance performance (by generating fewer and more efficient updating SQL statements)
back-reference not available in object same as above 'inverse' must be turned off otherwise there's no way to keep the collection intact.

3. NHibernate doesn't allow mapping classes (ClassMap/SubclassMap) to be defined for interfaces. As their name implies they only apply to classes. And actually it doesn't actually work in concept if mapping is created on interfaces as interfaces may not present relationships in a tree-shaped structure and there is obviously no way for typical hierarchical class mapping approach which NHibernate takes to work on relationships as such and since interfaces are purely abstract there is no need to do so. Since it doesn't matter if the class is an abstract or a concrete one, there should always be a workaround for that where we appropriately create abstract classes sitting between the interfaces that we intend to persist and the concrete classes that originally implemented these interfaces. All properties in an abstract class that implement certain ones in the interface that were originally required to be persisted are now persisted in the corresponding columns in the table for the abstract class, and in order to avoid duplicate columns, all mappings for the derived classes should not have the overriding versions of these properties persisted.


To be continued / updated

posted @ 2011-11-30 11:36  quanben  阅读(191)  评论(0编辑  收藏  举报