Wednesday, October 28, 2009

NHibernate Optimization - Part 2 - Data Access Object (DAO)

In the first post on NHibernate Optimization, mapping file configuration was discussed. This post will focus on code optimization in data access objects (DAOs).

Load objects by ID.
Object should be loaded by ID whenever possible to benefit from first-level cache (session cache). Whenever an object is saved or loaded it is added to NHibernate's first-level cache with the ID as the key. As a reminder when NHibernate loads an object, it first looks in the first-level cache, then query cache, followed by second-level cache and finally SQL is executed. Therefore, loading an object by ID will be very quick if it is already in first-level cache.

Sometimes the UI layer needs to request an object with a business key rather than a surrogate key. In this case, a thread-safe Dictionary with key=business key and value=surrogate key should be considered. The UI layer requests the object by business key from the service layer. The service layer finds the surrogate key in the Dictionary and uses it to query the DAO. This is a little extra work, but  can reap appreciable performance benefits.

Use HQL, DTOs, and/or hand-coded ADO.NET in bottlenecks.
Oftentimes the UI layer presents a screen that summarizes the state of many different objects, which can force many separate and expensive database calls that return much more data that is actually necessary. A good way to optimize this is to use HQL or hand-coded ADO.NET to SELECT only the required data, using aggregate functions if necessary, and return the data in a non-hibernate mapped data transfer object (DTO).

This post scratched the surface of code optimizations in the data access layer. Please stand by for upcoming code optimization recommendations.

Labels: ,


Monday, October 26, 2009

NHibernate Optimization - Part 1 - Mapping Files

NHibernate is a powerful object/relational persistence and query service that has deep market penetration in the Java space and is rapidly gaining acceptance in the .Net community. NHibernate is particular beneficial to domain-driven projects as developers can focus on good OO design and have the database automatically generated from the domain model. Since developers are focused on classes and not tables, performance can suffer because little thought is given to how SQL is generated and executed. This is the first post in a series that will focus on strategies to monitor performance, best practices, and optimizing N/Hibernate code.

The Problem
Improper data access code and/or hibernate mapping files can cause NHibernate to execute inefficient queries, execute the same queries over and over, and/or retrieve much more data than needed. The three main areas of concern when dealing with Nhibernate performance are the mapping files, data access objects (DAOs), and monitoring/profiling tools.

Mapping file optimization
Improper fetch strategies are the most common mistakes in Nhibernate mapping files. Many times developers simply copy and paste an existing mapping file and just change the table and column names without giving any thought to fetching strategies used. As a general rule, lazy associations should be used by default because no matter what fetching strategy is used, non-lazy relationships will be fully loaded into memory executing SQL queries if necessary.

The Nhibernate developer should read and understand the fetching strategy section of the reference documentation. Batch fetching and subselect fetching can significantly reduce the number of SQL queries executed.

Another mapping element that can speed up the NHibernate application is second-level caching. A good idea for all applications is to enable read-only cache on "look-up" data that never changes. Nonstrict-read-write works well for objects that are mostly read and seldom written. More on caching will follow in upcoming posts.


In summary, use lazy associations by default, choose the write fetching strategy, and include second-level caching when appropriate.


Labels: , ,


Monday, October 13, 2008

NHibernate Optimization

I recently spent some time optimizing a highly concurrent web application's database access code. This particular application uses NHibernate for its ORM.

This post enumerates some of the "lessons-learned".
  • Set show_sql to true. Viewing the SQL that NHIbernate runs while in development will help developers catch redundant or unnecessary database calls.
  • Use HQL aggregation. HQL aggregate functions are much more performant than loading all of the objects and aggregating with code in memory.
  • By default, enable lazy initialization. 
  • Use batch-fetching where appropriate. In a one-to-many mapping, if most of the time more than one item needs to be accessed in the "many" collection enable batch-fetching by setting the batch-size. The first time one item in the "many" is asked for NHibernate will prefetch "batch-size" items.
  • Use the right fetch strategy.
  • Retrieve objects by ID if possible. NHibernate has a first-level cache, which is only utilized when an object's retrieval is by ID.
  • Use second-level cache for read-only associations.
  • Multi queries can reduce database round-trips.
  • Hand-code ADO.Net. Use a SQL profiler and a code profiler to find bottlenecks.If a bottleneck is found that can't be fixed by changing the fetch strategy, HQL, or caching, dropping down to ADO .Net is probably the best option. This doesn't mean swapping out NHIbernate completely, but just the slow query.

Labels: , ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]