I thought it might be useful to write a quick reference for things that I find a bit confusing. I've been playing with the NHibernate ORM tool quite a lot recently - it's still being ported from the mature Java version of Hibernate, but on the whole I'm loving it, and the developers are doing a great job!
Please feel free to contact me if any of this doesn't make sense, or is plain wrong! Get me at tobinattobinharrisdotcom.
If you're wondering how to get at NHibernate, then following the link... http://nhibernate.sourceforge.net/
If you're using NHibernate with ASP.NET, then you may like to read my ramblings, they've come out of a tricky learning curve, which I'm still on I might add! NHibernate with ASPX
The only time objects get written to the database is when session.Flush(...) or a transaction.Commit() is called.
All other methods such as session.Save(...), Update(...), Lock(...) etc don't do nada in terms of writing to your db.
Note: when you do a transaction.Commit(), behind the scenes the transaction calls the Flush() method on it's owning session.
As default, the session will actually call the Flush(...) method itself from time to time. Of course, you can also call Flush explicitly youself.
I had a look at the source code for the Session, and found that the session will call the private method AutoFlushIfRequired() at the following times:
When the session.Find(...) method is called.
When the Filter(...) method is called
There may be other situations too, but these were the obviouse ones.
Note that the automatic flushing will only take place if the session.FlushMode is set to Auto. This is the default option.
Updated!
When the time comes to write to the db (see above), NHibernate needs to know weather to insert or update your object(s). Doing a session.Save(...) or a session.Update(...) will let NHibernate know what type of action to perform.
- Update(...) equates to a database update
- Save(...) equates to a database insert
You can also use session.SaveOrUpdate(...) to tell NHibernate to work out what is most appropriate. This applies to new objects only, since if the object was actually retrieved through NHibernate, it will automatically be tracking changes to it and will save it on the next flush.
Remember, Save(), Update() and SaveOrUpdate() do not do anything to the db, that comes later when you flush the session or commit a transaction. All your doing with Save and SaveOrUpdate is telling NHibernate to mark the methods as persistent. As Mikael Nyberg kindly pointed out...
"The easier (and correct, according to the Hibernate docs) way to think about what the Save method does is that it marks the instance as persistent (and in that process, assigns or generates and identifier for the instance). So if the instance is already marked as persistent (for instance, if it was returned from the Find() method), it is never necessary to call the Save method, Hibernate will keep track of changes and update as necessary. So Hibernate sort of writes its OWN todo list for objects that are marked as persistent."
[TODO, confirm this section is correct]
Example relationship - User has zero or more accounts. An account has 1 user.
In the User.hbm.xml, make sure you have the unsaved-value specified for its id. Add a bag or set or whatever in the mapping, and set inverse="true". Have a one-to-many element in there. If you don't want the parent to be accessible from the child in your objects public interface, then set the property to private or protected etc.
In Account.hbm.xml, make sure you have a many-to-one set up pointing back. I do this because there is an issue with nulls. [TODO, flesh out]
New!
In my Visual Studio solution explorer, I create subfolder called "MappingFiles" or something. I then create my hbm.xml files in there - one for each class. You need to remember to set them as an embedded resource, so they actually get compiled into the assembly. At runtime, NHIbernate will search the assembly for any embedded resources named *.hbm.xml and try to parse them.
I'd be interested to hear where other people have their Hibernate files live.
[TODO, confirm this section is correct]
Updated!
For an easy life check...
Session.Flush(...)
Transaction.Commit() //indirectly calls a session.Flush()
Find(...)
Load(...)
[TODO]
[TODO]
Hobernate sessions are generally not meant to be left open for long times, as far as I can tell. There are some good guidelines for ASP.NET environment here http://www.hibernate.org/168.html. ] [TODO: any links for winforms environment?]
Updated!
When you want to make several changes and be left with the option of rolling back if things don't go as planned.
As far as I can tell, NHibernate transactions are pretty much coupled to database transactions. So, if you start an NH tran, you're also starting an ADO transaction. This means you need to be aware of all those db related issues. More specifically, when using transactions:
All my code NH code roughly follows this pattern:
try
{
ThreadLocalSession.CurrentSession.BeginTransaction();
DoSomeUpdates();
ThreadLocalSession.CurrentSession.Transaction.Commit();
}
catch
{
ThreadLocalSession.CurrentSession.Transaction.Rollback();
throw;
}
finally
{
ThreadLocalSession.CloseSession();
}
I'm still getting to grips with it, but this gives me the following advantages:
Save() is only needed for objects that are not persistent (such as new objects). You can use Update to bring an object that has been evicted back into a session.
I need to do more test cases on this one. However, this works....
ITransaction t = s.BeginTransaction();
DirectoryEntry e = e = loadTestEntry( s );
Assert.AreEqual( null, e.OfficeAddress.Line3 );
e.OfficeAddress.Line3 = "Test";
t.Commit();
See how there are no Update(...) calls!? This indacates that you don't explicitly have to call update, but I need to get that confirmed...
Similarly, I've done something like:
ITransaction t = s.BeginTransaction();
DirectoryEntry e = loadTestEntry( s );
Role r = new Role();
r.Name = "Customer";
user.Roles.Add( r );
t.Commit();
Which also seemed to work. Notice how there are no s.Save( r ) to tell the session to save the new Role object? So, I'm guessing that NHibernate is automatically trying a SaveOrUpdate on these objects to save me the bother! Anyway, I need to bottom this one out so I fully understand it. I think that reading the original Hibernate docs could help....
Evict - this takes an object (and it's collections) out of the session so that it isn't written to the database when flush() is called.
Refresh - this can be used to force the session to reload the object form the database. Apparently this is good if, for example, a trigger might fire in the database that updates a record, and you want your object reflect that update.
TODO
Updated!
TODO
More ramblings about using with ASPX pages
here
[TODO]
I think the idea behind NHibernate is to work in terms of the object, so for database people the API may be unfamiliar. Database terminology doesn't really make too much sense in the object world, and if you're doing ORM, then you really want to be talking about objects rather than database records. For example, you don't select objects from memory, you load them. Similarly you don't really insert objects in to memory, you save them.
If you really don't get along with this terminology, then why not create a a wrapper for the session. You could present an interface such as:
public void Insert( object o ){...} //maps to Save()
public object Select( Type t, object id ) {...} //maps to Load()
public object Update( object o ) //maps to Update, doh!
public object Delete( object o ) // this is almost getting boring!
Personally, I'm trying to get used the the NHIbernate way. Simply becuase I want to get familiar with it's API.
NHibernate allso has the concept of transactions in it's API. Transactions shouldn't be to hard to mentally map into the object realm, since their underlying concepts (ACID) are the same everywhere. Basically, they represent a sequence of operations that are either done or not done. You can begin a transaction, do some work, then either commit it or roll it back. If you commit, the changes are accepted, or made permanent. If you roll back, the changes are discarded. So, how should this work in the NHibernate/object world? The same! If we commit, then expect any changes to your objects to be written permanently. If rolling back an NHibernate transaction, I would expect any changes made to any objects inside the transaction to be restored to their state before the transaction.
Transactions and sessions get a bit more tricky. Can a transaction exist across multiple session in Nhibernate? Also, what happens to objects that are not registered in the session, are these restored also? TODO
You do not have to subclass any kind of persistent base class to make objects persistable.
You do not have to decorate all your classes with attributes.
You do not have to provide any parameterised constructor in your business objects so that NH can create them
You do have to provide a public default (empty) constructor.
You do have to provide public property get/sets for all persisted fields.
...Apparently, these last 2 limitations are set to change I think. It must be possible in .NET, as the DADO mapper works this way. Personally, I think they're a small price to pay anyway. I can unit test how my business objects behave without even having to hit the database, simply by not using NHIbernate in those tests ( I have other tests to prove that my mapping files are set up ok)...
You do have to create an XML file that defines how things work.
...but this is really easy. AND, there are tools around to help you auto-generate your XML files, and also your classes if you want. The beauty of this is that if you design the persistent aspects of your system in the XML, then you can automatically generate both the database and the classes from this! You still need will have some work to do, but it's not far off.
Another bonus about the XML is it does give you a good feeling of control, which is important when going through a learning curve. The XML definition files make good sense so far, and are well documented. Personally, I think GUI tools are great time savers, and if available, should be used in the "production environment" to save time. However, when learning it's nice to see what's going on without it all being hidden away behind pretty buttons and pictures.
Ok, here's my bottom line on why NHibernate has excited me more than any other tools I've use. In a nutshell..
*** It's mature ***. Well, more specifically, the Java Hibernate system is mature, meaning it has...
The .NET port doesn't hold to all these claims, but I was pleasantly surprised when I could go and read something like this (http://www.hibernate.org/hib_docs/reference/en/html_single/) and actually start persisting objects in complex ways. WARNING: don't use that reference too much, becuase it's for a more advanced version of Hibernate than has been porting.
One other thing I like about NHibernate is it's error reporting. As I go through the learning curve, whenever I make mistakes in the mapping file, I get really useful runtime error messages back pinpointing what I do wrong. This is a realy eye opener, and reduces my learning curve/problem hunting time greatly. I have played with ORM tools that aren't so informative in the error department, and finding your mapping blunders can take forever!
Of course, another good point is that it's open source! This is nice because apart from not having to pay anything for it, I have the security of being able to fix bugs myself and add features to the code if needed.
Incidentally, I've also been looking at Entity Broker, a commercial product. This does look like a strong product in the world of .NET persistence, and has a lot of nice features. I haven't tried it, but there's a great feature where you can swap the brokers database connection string for a connection string to a broker on a remote server. So, you can achieve .NET object distribution without having to labour over DTOs, concurrency etc. As I said, I haven't tried it, but sounds great!
I'm not an architect, and hardly even a seasoned developer. However, I'm trying the following, which feels quite good at the moment.
At the core I have...
This is for an ASP.NET project that will have many separate web "applications" using the same business objects.
http://blog.aspcool.com/tim/category/6.aspx