…And we’re back again in Part 5 of the series. We’ve come to a decent, functioning (yet small) domain model. We did it by focusing on the domain and the requirements, and were not heavily influenced by persistence related concerns.
Let’s create a database structure to support persistence of our domain model.
When creating the database, you might realize that the database has no notion of class inheritance. This is a serious impedance mismatch and represents one (of many) difficulties with object-relational mapping. There are 3 main approaches to solve this problem (outlined in more detail in Hibernate In Action ) :
- Table Per Concrete Class – In this approach you ignore the inheritance and just map each type to it’s own table, including all columns contained in the superclass.
- Table Per Class Hierarchy – This options dictates that you should collapse the hierarchy into a single table, and use a “type” column to determine what kind of record it is. This is called a discriminator in NHibernate. Some people prefer this for performance reasons (no joins required to fetch a child object).
- Table Per Subclass – Create a shared key between a child table and the parent table, and define the columns separated by class. This is the approach I prefer. It’s the most normalized and also happens to be quite easy to work with in NHibernate. If I call Load on a Video to get it from the database, NHibernate will know to grab Name and UPC from the Items table, and the rest of the properties from the Videos table.
Here’s our Item inheritance hierarchy mapped to the database:
This option is completely normalized and maps elegantly (as you’ll see) with NHibernate.
Here’s the rest of the initial database structure (click to enlarge):
Take a minute to understand the table structure. I’m sure there’s some room for improvement, but this will suit us for now. You’ll notice that I kept a few things de-normalized for ease of querying and performance. For example a Transaction’s balance is the sum of the rentals, but if we control the adding and removing of rentals then we can keep this data in sync. We can then avoid querying for all of a transaction’s rentals just to get the total (think of displaying this data in a grid with transaction summaries… we’d avoid loading all of the rentals for each line in the grid). The same concept is applied to the Accounts table. An account’s balance is the combination of transaction totals, plus tax, minus any money paid. Late fees will also modify this amount. I chose to omit this kind of data to keep the example somewhat simple.
If you look at this ER diagram in comparison to the object diagrams in previous steps you’ll see first-hand the impedance mismatch of a relational model vs. an object-oriented model. By this point I hope you’ll agree with me that an object-oriented model is much more expressive and intuitive to work with. A well designed model will have a fluent interface and read very close to the language of the domain.
As a side note, this concept goes beyond just thinking in the typical “pick out the nouns – those are your objects” line of thought. Don’t get me wrong, that’s a great place to start… however you’ll get more benefit if you develop a model that makes perfect sense to business users. They will understand it and help you build it. They will likely find flaws in the model and help clarify. This bridge between developer speak and business lingo is what Eric Evans calls The Ubiquitous Language. For more on this topic I highly recommend his book Domain Driven Design.
To get our objects persisted to the database we need to map the objects to their relational tables. We need to support some common operations such as loading, querying, inserting updating, and deleting. And we’d like to do it without writing mundane, repetitive SQL statements or stored procedures. We need an object relational mapper.
Like I said in the beginning, I’m going to focus on getting started with NHibernate in a real project, and not to go over the easy stuff in too much detail. To use NHibernate, first you need to download the latest release from SourceForge. Remember: NHibernate is a persistence concern, so we should only add a reference to the NHibernate.dll in our data access project.
I’ve created a class library project called Flux88.Videocracy.Persistence which will hold all of our persistence related classes. I also created a project to house our tests for these classes. Here’s the solution now:
Ok, let’s switch gears for a moment and talk about how NHibernate works. Like most complex components, NHibernate requires configuration to work properly. You can do this through code, but I’ve found that it’s more flexible and easier to maintain when you add it to the application’s configuration file. We’ll get to that in just a moment.
We don’t have a UI yet, so we’ll really be calling all of this code in our test project. Since our test project uses these classes, it also needs to provide a configuration file. Configuration for test projects is pretty much the same as a WinForms project; all you need is an app.config.
I’ve created an App.config file inside Flux88.Videocracy.Persistence.Tests. Here’s a basic config file with a configuration section for NHibernate:
This is pretty standard configuration for NHibernate. I received some excellent tips on configuration from Billy McCafferty’s article on NHibernate Best Practices at CodeProject.
The next step is to read this configuration and instantiate a SessionFactory. Let’s stop and think about this for a minute. A SessionFactory exists solely to take the configuration and hand our ISessions to work with the database. This is expensive to create, so we only need to do it once for the entire application lifecycle. I also want to wrap the NHibernate details as much as possible so that I can provide a very thin coupling to it.
I’ll start this one out with a test. I’ll create a SessionSource class (to borrow a name from an influential developer) that will wrap the SessionFactory. It will need to be initalized before use and be able to return Sessions when we need them.
Here’s a test for the initialization:
It’s a simple enough test, but it actually tests a lot. In order to initialize our session source we must read the configuration file. Each required property must be set in order for the session factory to be created.
I had to add a reference to the DomainModel project and the Persistence project in my Persistence Tests project to get the configuration to be able to read the “Flux88.Videocracy.DomainModel” assembly reference.
Combined with my configuration above, this is the code that satisfies the test:
We can either explicitly call Initialize when our application starts, or we can defer it until it is first needed, either one. I like to have this flexibility because it helps out in testing and also makes it easy to work with in our application as well.
Next up we actually need to be able to return an NHibernate ISession to be able to do any work with the database, so we have:
Here we show the lazy initialization (if needed) and just a call to open a new session. I also included an overload (not shown) to accept a custom IInterceptor and pass it into the factory’s OpenSession call. We’ll talk about IInterceptors at length in a later article. And before I go any further in code, I need some code to test this method.
So at this point we’ve covered the database tables, NHibernate table mapping strategies, NHibernate configuration, and a simple NHibernate helper class to assist with the usage of NHibernate.
This is a good place to stop for now. We’ll build off of this SessionSource next time, as well as introduce Repositories for our domain entities.
As always I’d love to hear feedback on these articles. Are you finding them helpful? Are they too simple, too confusing, too fast, too slow?