Classification


OK, I have introduced NHibernate, and we have had a general look at how it deals with the requirements. We are now ready to investigate NHibernate further, and this time with the help of the disposition used in Chapter 8. But instead of discussing the alternatives in an abstract way, I will now position NHibernate as being an example of the alternatives to each category and also discuss how the solution looks in a little bit more detail.

First up is the Domain Model style that NHibernate expects us to use. We have already touched upon this topic, but let's say a few more words about it.

Domain Model Style

I said in Chapter 8 that the three most usual aspects for Domain Model styles that O/R Mappers use include the following:

  • PI

  • Inheritance

  • Interface implementation

Life isn't black and white, but in this case I think the choice is easy. The way I see it, NHibernate gives us a high level of PI. You don't have to inherit from certain base classes, and you don't have to implement certain interfaces. You don't even have to reference any NHibernate assemblies in your Domain Model.

Mapper Style

When it comes to Mapper style, I said that Metadata-based mappings are often exemplified in two different ways:

  • Code generation

  • Framework/reflective

Again, it's pretty easy to position NHibernate because it's a typical example of a Framework/reflective solution. Sure, there is some code generation support in NHibernate, but that is typically used when generating one of the three parts of the Domain Model, Metadata or database schema by looking at one (or two) of the other parts. For example, assume you have the metadatafrom that you can create database schema. That's not what I meant by code generation as the mapper style. The mapper style is defined by how the mapping is done at runtime, and for that, NHibernate doesn't use code generation.

One exception to the rule is when we look at what is done to support Lazy Load [Fowler PoEAA]. Then at runtime NHibernate will swap, for example, your used list class for some proxy instead. In order for this to work, you have to expose the list as an interface and not as a concrete class.

Note

Note that I didn't mention the usage of interfaces as a means of lowering PI level earlier in the chapter. The reason for this decision was that Lazy Loading is a feature and not something you have to use.

The opinion on that varies, but as I see it, Lazy Loading is an optimization technique and not something you have to use all the time.


Is it good or bad that NHibernate is using the Framework/reflective approach? I really believe that it is mostly a good thing when it comes to O/R Mappers.

Starting Point

When you are working with a certain O/R Mapper, you can often choose one or more starting points from the following list:

  • Database schema

  • Domain Model

  • Mapping information

NHibernate allows you to start with any of these, and it's not just a hack, but it's supported by design. With that said, if you have to start from a legacy database schema that you aren't allowed to change, having a look at something like iBATIS [iBATIS] might be a good idea.

API Focus

The API focus comes in one of two alternatives:

  • Tables

  • Domain Model

But I said that this doesn't really apply when it comes to the most common O/R Mappers because their main purpose is to provide a Domain Model view of the world while using a Relational database for the persistence. So the answer here is easy. NHibernate uses the Domain Model as the API focus.

Query Language Style

Now it gets interesting. The querying capabilities category is a category where the various solutions differ quite a lot. The subcategories I defined in Chapter 8 included the following:

  • String-based, SQL-ish

  • Query Object-based

NHibernate has implementations for both of these styles, called HQL and Criteria objects in NHibernate. I have already given an example of what an HQL query could look like when searching for all Customers whose names start with "Vol". If I want to express the same thing with Criteria objects, it could look like this:

//A consumer IList result = _session.CreateCriteria(typeof(Customer))     .Add(Expression.Like("Name", "Vol%"))     .List();


In the previous snippet, you also saw an example of chaining of several method calls. It works because Add() returns an ICriteria just as CreateCriteria(). This helps to make the usage of criteria queries slightly more readable.

Note

In this chapter, I'm just touching briefly on how different things are accomplished with NHibernate, but in no other case is my briefness so apparent as when it comes to the huge topic of querying. Please refer to [Bauer/King HiA] for more information.


In Chapter 8, I also mentioned that it's good to have the possibility to go over to raw SQL as a last resort, typically to solve performance problems, and you can do that with NHibernate. Both variants are supported so that you can get Entities back and you can get to the raw connection and do whatever you want to.

NHibernate also provides something in between the ordinary query mechanisms and raw SQL that they call report queries (also sometimes called flattening queries). It can look like this in action:

//A consumer string hql = "select new CustomerSnapshot(c.Id, c.Name) " +     "from Customer c"; IList result = _session.CreateQuery(hql).List();


So here the result won't have Customer instances, but CustomerSnapshot instances. For it to work, you need to provide a matching constructor on the Value Object (CustomerSnapshot in the previous case) and to mention CustomerSnapshot in a mapping file via an import directive like this:

<import  />


Note that CustomerSnapshot just holds on to flat data (typically read-only) according to NHibernate. Identities won't be tracked in the Identity Map, and no changes will be tracked in the Unit of Work.

Talking about querying, that's actually exactly what the next category is about as well.

Advanced Database Support

The examples I provided in Chapter 8 for "advanced database support" (not advanced for a database guy, but perhaps for an object guy) follow:

  • Aggregates

  • Ordering

  • Group by

  • Scalar queries

All four of these are supported by both HQL and Criteria objects in NHibernate. Let's take an example of each, beginning with an aggregate query finding the number of instances of Customer. In HQL, it could look like this:

//A consumer string hql = "select count(*) from Customer"; int numberOfCustomers =     (int)_session.CreateQuery(hql).UniqueResult();


Note

Again, note that it's not table names but classes that are used in HQL.


The second example, to order the result set in the database so that it doesn't have to be done in the Domain Model (which might often be a good thing), could look like this in HQL. In this case we are ordering the fetch operation of all customers by Name.

select from Customer order by Name


Group by is mostly used for reporting purposes, but you could still use it in your application. Here I'm grouping on Name and counting how many instances for each name (a bit twisted, but it shows the syntax):

//A consumer string hql =     "select c.Name, count(*) from Customer c group by c.Name"; IList result = _session.CreateQuery(hql).List();


Finally, an example of a scalar query could be to only fetch the Name of the Customers whose names start with "Vol" (which by now you'll recognize as my favorite criterion), but as a matter of fact you just saw another example when I executed the group by query earlier. To get to the values in the result, the IList called result contains a list of object arrays. Here's a snippet for continuing from the previous example, listing the names and the number of instances for each name:

//A consumer, continuing from previous snippet foreach (object[] o in result) {     Console.WriteLine("{0}  {1}", (string)o[0], (int)o[1]); }


Scalar queries are especially useful in situations where you find it too expensive to fetch complete Customer instances but want a more lightweight result, and you don't want to define a class.

Of course, the result won't be typesafe in this case, but you could use a variation, namely the one I talked about earlier in this chapter called report query (or a flattening query).

An alternative is to provide mapping variations, but then I think it's an even bigger thing you have done. That is obviously violating the Aggregates because you are making it possible to update the instances without making it mandatory to load the complete Aggregates. Warning bells should go off.

Let's end this section with something that might be "obvious" to you if you come from an object background, but might look like magic if you come from an SQL background.

select o.Customer.Name, count(*) from Order o group by o.Customer.Name


The query itself was very simple, but note that data is fetched from two tables, and there is no mention about that in the from clause (that is, there is no join). Of course, the mapping information is used to determine what needs to be done at execution time with the HQL query.

Other Functionality

Finally, we have a mix of other functionality to check for NHibernate.

  • What backends are supported?

    This is a typical example of something that could become a long and tedious feature enumeration, so I won't go into detail here. Let's just say that NHibernate supports several different databases (most of the major commercial and open source databases). Check the site [NHibernate] for current information. You can also write your own plugin if you miss the database you need to work with.

  • Not just an O/R Mapper

    NHibernate is pretty focused on being an O/R Mapper and nothing else. The philosophy is to do one thing and do it well.

Note

Of course, NHibernate does other things as well, but those other things are just not the focus. And they are not too many or too big either.


  • Advanced caching

    Advanced caching is one area where NHibernate isn't really up to par when compared to Hibernate, but I think it's rapidly becoming better. On the other hand, to me that's not usually a big deal. There are many problems with second-level caching, so that option is often ruled out.

  • Advanced support for controlling transactions

    Support for manual transaction control is pretty good in NHibernate. There are situations where you might be taken by surprise if you don't watch out (for instance, when you execute a query in the middle of a transaction, NHibernate will then Flush() changes to the database before executing the query). That's a tradeoff between querying not going "through" the cache or ordinary transaction control. The good news is that it's controllable by your code.

    To bind this to Chapter 8, the typical way of dealing with transactions in NHibernate is "manual control."

Note

On top of that, you can also build something that uses any of the more automatic approaches mentioned in Chapter 8.


  • Open source

    As I've said already, NHibernate is an open source project, so you can investigate the source code and even build your own branch.

  • Version (generation)

    It's hard to decide what version NHibernate is in. At the time of writing, it's called version 1 by the team building it, but at the same time, I would say that the quality is higher than with typical first versions. NHibernate is also a port from Hibernate, which is second generation or actually third now, but that's not reflected in NHibernate. Anyway, let's call it generation 1.

Let's have a look at how NHibernate positions itself in the classifications from a totally different angle: the infrastructure patterns angle.




Applying Domain-Driven Design and Patterns(c) With Examples in C# and  .NET
Applying Domain-Driven Design and Patterns: With Examples in C# and .NET
ISBN: 0321268202
EAN: 2147483647
Year: 2006
Pages: 179
Authors: Jimmy Nilsson

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net