Optimizing NHibernate

time to read 3 min | 433 words

Aaron (Eleutian) is talking about some issues that he has with optimizing with NHibernate.

So in short, I feel NHibernate (and any ORM for that matter) needs the following features to really be optimization friendly:

  • Lazy field initialization
  • Querying for partial objects: select u(Username, Email) from User u
  • Read-only queries that do not get flushed.
  • Join qualifiers (on in T-SQL)

Let me try to take this in order.

Lazy Field Initialization:

On the surface, it looks very good, because you can do something like:

Customer customer = session.Load<Customer>(15);
Console.Write(customer.Name);

And the OR/M would generate this SQL:

SELECT Name FROM Customers where Id = @p0; @p0 = 15;

That sounds fine, until you realize that the database roundtrip is far more expensive than loading a single row, even if you load all its columns (leaving BLOB aside for now). This means that it is usually much more efficient to load the entire row at a single go, rather than piecemeal.

I tried to toy a bit with the way you would do that, and I can't really think of a good way to handle it without causing major management issues for the OR/M user.

That said, it is a feature that can be very valuable if you make it optional, per field basis. That way, you can have a customer object that contains a Photo BLOB column, and have it accessible only when needed, yet keep the natural programming model.

Partial object queries

Well, NHibernate has that:

select new UserSummaryDetails(u.Username, u.Email) from User u

It will return a list of UserSummaryDetails that you can use. As an aside, they are also not tracked by NHibernate.

Real Only Queries

Those are actually fairly simple to implement, you just need something like:

ISession tempSession = factory.OpenSession( currentSession.Connection );
tempSession.FlushMode = FlushMode.Never;
results = tempSession.CreateCriteria(typeof(Customer)).List();
tempSession.Dispose();

Abstract that to a helper function and you are set.

Another option is to use SetResultTranformer to inject an modifier that will evict the instance from the session (which has its own issues).

Join Qualifiers

From a few experiments that I have done, there is not difference on the query plan if you are using ANSI joins or where clause joins. This is one solution to the problem. Another issue would be what syntax to choose. NHibernate would need to map that to all the relevant databases, which may not always support ANSI joins.

Not simple answer there, but the HQL Parser that we are building should make it more accessible to developers to go in and change it.