Tobins' NHibernate FAQ

Tobins' NHibernate FAQ

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

Q) When do objects get written to the database?

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.  

Q) So when does the session automatically flush itself?

 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

Q) How do I control whether NHibibernate updates or inserts an object in the database?

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."

Q) What are the minimum things I need to do to set up a reliable one-to-many association

[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]

Q) Where do the .hbm.xml files live?

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.

Q) Things to check in the *.hbm.xml files.

[TODO, confirm this section is correct]

Updated!

For an easy life check...

  • Is the .hbm file embedded into the project?
  • Are all unsaved-values specified?
  • Do many-to-one relationships have an inverse attribute, with the relationship handled at the other end? (TODO)



Q) What methods cause NHibernate to talk to the database?

Session.Flush(...)
Transaction.Commit() //indirectly calls a session.Flush()
Find(...)
Load(...)
[TODO]

Q) When should I open a new session?

[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?]

Q) When should transactions be used?

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:

  • Keep them as short and simple as possible
  • Don't request any input from user during the transaction, if the user takes 10 minutes to respond then you're locking db objects for 10 minutes!
  • Flush the NH Session before running transactions.

Q) How do you manage exceptions?

All my code NH code roughly follows this pattern:


//may call ThreadLocalSession.CurrentSession.Save(..) etc
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:

  • Transactions are rolled back on error, to stop them locking DB resources.
  • Any errors that are caused in DoSomeUpdates() get caught too. I can add several catch blocks for appropriate handling.
  • Finally, the session is closed to keep resources open only for as short time as possible. Remember, creating a Hibernate session is not an expensive operation so closing it frequently doesn't do much harm.


Q) Do I still have to do Save and Update inside transactions?

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....

Q) What do Evict and Refresh do?

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.

Q) Does the session implement the Unit of Work pattern?

TODO

Q) How do I use NHibernate with ASPX pages?

Updated!

TODO

  • Read this... http://www.hibernate.org/168.html
  • Implement Thread Local Storage Pattern
  • Don't stash session in the ASP.NET session, or at least not for too long.
  • Don't have transactions spanning multiple HTTP requests - this will lock the db objects.

More ramblings about using with ASPX pages
here

Q) What is the lifecycle of a session with respect to database interaction?

[TODO]

Q) The NHibernate/Hibernate API seems to confuse people, should we ask for an alternative, more intuitive API?

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

Q) I'm thinking about using NHIbernate, what affect will it have on my design/implementation decisions?

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. 

Q) What, in your humble opinion, does NHibernate have going for it?

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...

  • been through numerous iterations
  • has had bugs beaten out of it 
  • had loads and loads of great features added
  • has been used in real projects for ages
  • has tons of documentation  

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!

Q) What architecture are you using with NHibernate?

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...

  • Business Objects - The MyProject.BusinessObjects dll contains plain old c# (POC#) objects that represent the business entities. These tend to be the ones I want NHibernate to persist, but there are also some classes in there that are not persisted (aka transient). For the current project, I have also put an ObjectDb class in this dll that provides easy access to NHibernate. So, all my persistence is handled in this layer. Oh, I've also have 2 sets of unit tests in here so I can test both the NHIbernate persistence stuff, and also the domain logic itself. As you can guess, this isn't a massive project so I'm keeping it simple!
  • Service Objects - I'm creating service objects who's purpose in life is to provide the functions needed to support a given use case.  This way my GUIs and other apps can reference the MyProject.Services dll, and access the service objects to help them do stuff.  The service objects talk directly to the Business Objects. Also, they are responsible for controlling transactions.
  • Data Access Objects (DAOs) - Another thought I'm having is that I may also write some Data Access Objects. These would probably live in the BusinessObejcts dll. For cases where I need to have various queries, it may help me to have a DAO that holds them, so not to clutter up the business objects themselves. For example, CustomerDAO may have methods such as CustomerDAO.FindRepeatCustomers(); These might contain Hibernate (HSQL) queries. I haven't needed these DAOs yet, but get the feeling I might.

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

http://www.cnblogs.com/wljcan/

http://nhibernate.sourceforge.net/quickstart.html

你可能感兴趣的:(Hibernate)