NHibernate: Collections of components - an alternative

added by notherdev
12/30/2011 9:33:28 AM

9 Kicks, 371 Views

When we have a model where an entity has some properties collections used for displaying it to the customer and we are not querying our data by these properties at all, it may seem right to map these collections in NHibernate as collections of components. Component in NHibernate is an object without its own identity, being a part of its parent entity. Our collections sounds similiar in terms of lifetime and ownership, but different in terms of relationship - these are one-to-many. In complex scenarios, additional queries became a serious issue. We've decided to try another solution - it's so simple that it can't go wrong.


3 comments

dpeterson
12/30/2011 9:35:17 AM
What's the purpose of serializing the data into a single column? You mention that you don't need to query it, so it's ok to break normalization, but I can't see how that could possibly be faster/easier. Do you have any performance benchmarks? Doesn't it take more code to deserialize the data than to just work with another table?

notherdev
12/30/2011 9:46:50 AM
The purpose is mentioned in the article - in case where there are several collections like that, we are issuing database queries (or strange one-to-many join) for each collection just to display the single entity data.

Moreover, tables generated by NHibernate for the components doesn't have primary key. This is pretty obvious, but it's generally a bad practice and can be a performance pitfall when used in transactions, as transaction logs can't work with full efficiency without primary key. Cost of serializing and deserializing is equal to string concatenation cost - I don't have any benchmarks, but I believe it is a level of magnitude faster than additional database roundtrip.

dpeterson
12/30/2011 9:49:13 AM
It's odd that NHibernate would do several round trips to fetch that data. Does disabling lazy instantiation help the problem? It seems like perhaps the problem is with the concept of components, maybe discrete entities would offer better performance/ease of coding?

notherdev
12/30/2011 9:54:11 AM
The problem is not with lazy loading, it is that we have many components for single entity. NHibernate can fetch it in single roundtrip using join, but that will be joining 1 row with N rows - seems quite unnatural for me. Joins usually go other way round. And when we have multiple collection, we'll need to fetch N*M*... rows joining more and more tables - not good for sure. Entities would help here for sure, but it breaks other assumption in our - that our collection values are dependent on parent entity (to keep the model clean).

dpeterson
12/30/2011 10:01:54 AM
This seems like one of those times when the ORM is fighting you. If we were to solve this problem using datatables/datareaders in ADO there would be no problem retrieving all this data. Surely there must be some way for you to tell NHibernate to efficiently retrieve all that data and set up the objects for you without making several round trips?

notherdev
12/30/2011 12:12:32 PM
Hmm, but how would you retrieve single company together with its 10 addresses and 15 managers names in a single query? With joins we'll have 150 quite wide rows, I'm not sure this is a good idea and I can't think of any other way.

dpeterson
12/30/2011 12:31:40 PM
Well, if you set up addresses and managers as having foreign key relationships to companies, wouldn't NHibernate just issue 3 queries out of the box and retrieve the collection of addresses and managers for a given company? That's how I'd do it if I were doing it by hand (no ORM).

notherdev
12/30/2011 12:33:37 PM
Yes, as I've written in the article, that's what I did at the beginning - but that's 3 queries instead of 1.

dpeterson
12/30/2011 1:11:32 PM
Ah, I thought that you said that they didn't have primary keys because they were components, not full entities, and that was hurting performance. So would making them into entities, rather than components, solve that problem?

I would imagine that for 10-15 rows your solution would work ok, but beyond that I think it would just get slower and slower (not to mention you have to lock all that data just to update a part of it, and that you run the risk of accidentally destroying all of it).

notherdev
12/30/2011 1:18:16 PM
Yes, making them into entities solve the problem, but logically these are not entities, they don't have its own identity and lifecycle. My solution solves both issues - it's efficient AND conceptually clean.

dpeterson
12/30/2011 1:27:46 PM
Well I prefer to treat them as entities myself, but that certainly doesn't mean your solution doesn't work for your use-case. I'd be interested to see what other feedback you get.

Thanks for sharing!

pwhe23
12/30/2011 2:55:19 PM
I wonder if this would be a case where nHibernate Multi-Query would help? It should be able to execute multiple queries against the database and return multiple result sets to the cache in a single call without using joins.

http://stackoverflow.com/questions/5634483/nhibernate-multiquery-for-eager-loading-without-joins

notherdev
12/30/2011 3:18:08 PM
Indeed, it seems interesting, I'll have to look closer to this solution. Thanks!