NHibernate – not all that glitters is gold
Manuel Abadia has some criticism on NHibernate that I wanted to respond to. First things first, though, it seems that I have been hasty in my response to a problem that Manuel reported, NH-1123. I really don't like it when other people do it to me, and I would like to apologize to Manuel for treating him in a manner I don't wish to be treated.
As an aside, I have created a new bug on the JIRA, with a bit more focus on the underlying problem. This bug has now been fixed, and I would like to thank Manuel for (a) finding it, (b) persisting even after I wasn't as attentive as I should. Interesting bug, by the way.
He had several other things to say, which I want to respond to in details:
1) It is heavily tied to the java version (Hiberante), so a lot of the request and/or enhancements are ignored. If the java version is not going to add it, probably it will not be added to NHibernate.
I wouldn't say that, NHibernate is focused on bringing us up to par with Hibernate, but that is certainly not saying that we aren't going to add things that Hibernate doesn't have. Right now, MultiQuery and MultiCriteria are stuff that Hibernate doesn't have and we do, and there are others.
At the same time, if a feature request is something that we can either develop from scratch or port from Hibernate, we would rather bring it from Hibernate. The main reasons are to make it easier to use documentation, built on already fixed bugs, maintain knowledge, and more.
2) Hibernate was started in 2001, and was designed to overcome a lot of the ORM problems. During these years, it has solved a lot of those problems, but other still remain and they can’t be fixed without heavily changing its design.
There are certainly things that I would change with NHibernate/Hibernate if I were able. One of the things on the very top of the list would be to turn the entire internals to AST manipulation and delay the actual generation of SQL to just before executing the query / command. We are working on that, actually. And you are more than welcome to join in and help us do so. And yes, I do mean you, personally, dear reader.
I would be interested to learn what the other problems are, by the way.
3) Even if it is extensible, the IInterceptor interface does not expose all the events that can be interesting to capture (For example, events after loading, updating or deleting data). The only way to handle that at the moment is to implement the ILifecycle interface by the entity itself, losing entity transparency.
Fabio Maulo has been doing tremendous amount of work on this very topic, and NHibernate 2.0 will have the full power of events and listeners from Hibernate 3.0. Most of the world have been completed, and can be found on NHibernate trunk, it opens quite a bit of possibilities.
4) No paging support in some nontrivial scenarios.
As I said, the bug was fixed, but even without that, I would still say that this is not correct. There are quite a few ways that you can do paging in NHibernate, the default approach is not always the best one.
5) There is no support for retrieving all the data at once for hierarchical queries
Here is one example of how to do just that :-)
Okay, that was cheating, since it used native SQL, but that did worked through NHibernate, and you got live entities back from it. No, NHibernate has not native support for doing hierarchical queries, and to support them we have the prerequisite of AST based internal, most probably. (At least the way I think about them)
There is also multi queries, which is what I use to do hierarchies all over the place, mainly because they are far cheaper than a single hierarchical query in real world scenarios (7 tables representing hierarchy, in my case).
To surmise, I would like to mention that NHibernate is an open source project. By its very nature, you are able to go into the code and fix things yourself! NHibernate's codebase is fairly big, but it is also well partitioned, so you are going to find it much easier to work with it than you would expect by simple size estimates.
If you have a problem, by all means, please report it, but do give a try at fixing it, that is going to be easier than you imagine.
Comments
If there was a way to group together the calls made in an object graph, it would help tremendously, it's still too chatty.
I have no idea how this can be improved, but it would help the negative perceptions of ORM's.
Steve,
There are MANY ways to do this, actually.
What is the scenario?
Hi Ayende,
I thinks to treat with paging support (and other interesting stuff) for NHibernate, uNHAddIns it's a good approach, created for Fabio Maulo.
http://code.google.com/p/unhaddins/
Regards
Ayende,
I replied to you here:
http://www.manuelabadia.com/blog/PermaLink,guid,736ddc0b-dc70-492f-aeff-ba09ebdeb511.aspx
Let me just make a simple one:
(Let's assume no lazy load here)
you have a Company with a list of Employees.
one to many
when I turn on nhibernate sql debugging I'll see like this:
select ... from Company where ... companyid = ?
select ... from Employees where companyid = ?
Two calls.
Let's say I wanted one call that was a join ?
Steve,
You need to configure this at the mapping level:
IIRC, it is something like:
<set fetch="join">
"One of the things on the very top of the list would be to turn the entire internals to AST manipulation and delay the actual generation of SQL to just before executing the query / command."
I certainly agree with using an AST for all manipulation [* ahem * DOM over string manipulation, right ;-)] but why wait until the last moment before turning it into its final state, the sql string? Isn't it better to do that as soon as possible to allow the GC to collect all the objects from the AST?
/Mats
The idea is that you want to delay that as late as possible to allow transformation of the AST at all levels.
Ok, means we agree. Could I also suggest you generate a Sql Dom instead of strings from the AST? (Again, DOM over string).
/Mats
Mats,
I don't think that I understand the different between AST and DOM
I meant that first you parse the HQL, that will give you your AST (which is indeed a DOM). From there, you /could/ go right ahead and generate the resulting SQL strings directly. What I'm suggesting is that you don't do that, but instead (create the classes for and then) generate a SQL DOM from the AST. Then, finally, you generate the SQL strings from the SQL DOM - which can be done using visitors (where you can use different visitors for different databases). Makes sense? ´But perhaps NH is already doing this?
/Mats
Okay, so you are talking about HQL AST and SQL DOM.
I don't really see the need to create two representations of the same thing. We could probably generate the SQL directly of the HQL AST.
The AST is another representation of the "same thing" (the HQL string). Why do you see the need for that? /Mats
I take a query (HQL, Criteira, Linq, etc) and generate an AST, process it through everything that we may need, and finally generate SQL from it.
That is the overall idea.
Mats: delaying the SQL generation till the final moment allows you to add/remove/manipulate elements in the query specification elements at various levels. I do that too. For example the eager loading/prefetch path stuff manipulates the query specification objects to optimize the node queries based on the data known at that moment. If the query would have been generated early on, it would have come down to SQL string manipulation which sucks :).
Another advantage is that you can centralize the tree optimizations for all databases and simply have 'sql generators' per database.
Steve: wanting a single query for 1:n related entities isn't always what you want: the PK side will get duplicated in the rows, and this can be potentially be a lot of times, which means that the amount of data returned is huge.
In Objectspaces they had a trick where they used a union query which unioned the query per node to the main set and had NULL values for the fields from the entities not fetched in that node, so if you wanted Customer + Order (1:n) you had 1 select with the customer fields and NULL values for the Order fields UNION 1 select with the order fields and NULL values for the customer fields. This would be still 1 resultset but not the data explosion of the duplicate rows.
Of course, having a multiple branched tree would give a lot of problems.
Frans,
I absolutely agree about delaying the creation of the SQL until all the manuipulation has been done. All I'm saying is that the last possible time is probably going to be a bit before the SQL is actually used, and the earlier you can let the GC collect the AST, the better.
Ayende,
Yup, I understand, I'm just suggesting that your last step, "finally generate the SQL" be turned into a two-stepper: First generate an SQL DOM and then use visitors to spit out the SQL string from there.
One reason for this is that it allows you to isolate many of the db differences to this last step, using different visitors to support different databases and allowing you to move complexity out of the AST-SQL transformation phase.
Take joins for example. Your SQL DOM would only have one format for joins, meaning that the AST-SQL transformation would only have to work with one join format. Then the different visitors turn the SQL DOM joins into the correct join syntax for their respective databases.
I just think that a year from now, you may be posting "There are certainly things that I would change with NHibernate/Hibernate if I were able. One of the things on the very top of the list would be to turn the entire internals to ****SQL DOM manipulation and delay the actual generation of SQL to just before executing the query / command."
/Mats
Comment preview