(Prequalifier, this is still a subject of much Debate, please toss in your thoughts into the comments. These are Tips that I've run across that I wanted to share, it's not to say that this is the best way to do something every time, and should not be considered the catch all solution)
1: <id name="Id"
2: column="PrimaryTableID"
3: type="System.Guid">
4: <generator class="assigned" />
5: </id>
6: <version name="Version"
7: unsaved-value="0" />
I would also recommend to have all of your objects generate the ID in the non-default constructor (you typically want to leave an empty default constructor for NHibernate for it to load an object back from the database without any "Object initialization" logic)
The above mapping is, in my opinion, the most flexible ID setup to use with NHibernate. Why?
- NHibernate never has to go back to the database to "find out" what the ID is for the object, which is a very expensive operation on an Identity generation setup. A Hi-Lo setup would be a decent second choice to address this concern
- Assuming you generate the ID in the non-default constructors, the ID of an object created anywhere is going to be consistent, whether you're outside of a session (such as in model tests), or in a session using hydrated objects
- It significantly simplifies the equality checks you're required to do. As someone commented in my previous post, if you're unable to rely on having a unique ID at all times, your equality logic has to start incorporating additional rules to account for the pre-persistance case scenario's
- Version manages when an object should be inserted through a cascade or not (as opposed to an unsaved value attribute). Version is also handy to have for other reasons that I'll bring up in a later post.
A couple of cons:
- Looking at Guids in the database is not a pleasing experience, but really, you shouldn't be interacting with the database at that level. It's an implementation detail.
- Using Guids in the database can increase DB fragmentation, but really, that's where I feel the Database Engine Tuning advisor (if you're using MsSql) or a DBA steps in. There are ways around this, and as far as we've been able to tell with millions of rows in a production application, the differences seem to be almost un-noticable. Still, there's no way to get around it, it's not the best performing scenario on the DB side.
To be honest, I was originally on the side of continuing to use Identity, although I was looking at a hi-lo generator algorithm with it. My background is in a more DBA-ish field, so the concept presented above was like garlic to a vampire.
Times, they be a-changin. We need to let go of some of our old DBA habits in favor of emerging frameworks/concepts which can hide persistence complexities.
3 comments:
Huge improvements to the Guid index thrashing problem were fixed in SQL 2005 and with DB tuning and guid.comb algo's page fragementation is really a thing of the past.
Good points :) I view the guid vs int debate as a pre-optimization debate. You really don't know how your application will perform until you have it up and running in a production environment. If Guids are, for some reason, causing performance issues, then you should be able to identify that problem, and if no other solution with guids (such as tuning or guid.comb) will work, you could go back to using ints.
Otherwise, I feel that the complexity of the model is increasing purely to compensate for database implementation details.
another pro is u can allow external apps to create their own ids too. for example we build a lot of RIAs using flex/air if that UI wants a new object it can create it's own id without the need for a service call and subsequent db hit, much more efficient when you have a network layer involved with a smart interface technology
Post a Comment