Common nHibernate exceptions and a question

I often get asked question about nHibernate issues the question discussed last in this post is often brought up. But lets start out with a few simple exceptions that can sometimes take up more of your time than you would like.

"NHibernate.MappingException : Unknown entity class: MyNamespace.With.Persistent.Class"

You are trying to use a class as persistent that nHibernate wouldn't know how to handle. The number one reason for this error is that you have mapping files as embedded resources and that you forgot to actually embed the mapping file.

"NHibernate.MappingException : persistent class ClassName not found" or "NHibernate.MappingException : associated class not found: ClassName"

This is usually an error in your mapping, nHibernate can't find the class you reference in your mapping. This is often caused by typos or missing to specify in which assembly or namespace your persistent class is to be found. You can specify a default assembly and namespace in the <hibernate-mapping> element or you can write the name of your classes with the assembly and namespace specified on the form "Namespace.ClassName, Assembly".

"NHibernate.InvalidProxyTypeException : The following types may not be used as proxies"

This is a classic, if you want to use nHibernates lazyloading features you need to specify your classes properties as virtual so nHibernate can create proxies for you. If you don't need to use lazyloading on specific type mark these types with lazy="false" in the class element, and you won't have to mark its properties as virtual.

So that's a handful of the most common exceptions when starting out with nHibernate. Lets move on to a question I have answered quite a few times. People often want to minimize the number of roundtrip's to the database by joining and retrieving larger resultsets and have nHibernate transform this larger set of results into the correct object graph. Whether this is a good optimization depends heavily on the expected data since it could result in a huge amount of redundant data transferred between database and application. But nonetheless the thing that often tricks people is illustrated by the following test:

//Assumme we have 2 invoices with 3 invoicelines each
IList<Invoice> invoices = s.CreateCriteria(typeof (Invoice)).SetFetchMode("InvoiceLines", FetchMode.Join).List<Invoice>();
Assert.AreEqual(2, invoices.Count);

This test will fail, because you actually get 6 invoice entities back. That's because the resultset contains duplicate invoices (one for each InvoiceLine) and nHibernate transforms these into invoice instances. One way to fix this is to specify that you want distinct root entities back. This is done by specifying a result-transformer like this:

IList<Invoice> invoices =
                s.CreateCriteria(typeof (Invoice)).
                SetFetchMode("InvoiceLines", FetchMode.Join).
                SetResultTransformer(CriteriaUtil.DistinctRootEntity).
                List<Invoice>();

So what if your data isn't suitable for joining, you might end up with the large resultset I mentioned earlier? And we don't want to hit the N+1 Select problem by selecting the InvoiceLines for each of the fetched Invoice's. That's were nHibernates batch fetching can be used. We have retrieved all the Invoices we want to use and afterwards we want to select the invoicelines for those. We can specify that our InvoiceLines should be retrieved in batches by specifying a batch-size on the collection in our mapping file.

<set name="InvoiceLines" batch-size="20">
	<key column="InvoiceID" />
	<one-to-many class="InvoiceLine" />
</set>

I just used 20 as an example, this means that nHibernate will fetch the InvoiceLines for 20 Invoices at a time. So this will result in two roundtrip's(in our example case), and this without fetching duplicate data in the resultsets. You could optimize some scenarios like the one above using MultiQuery or MultiCriteria, that is sending multiple queries in one roundtrip, but that's another story.


Comments

May 15. 2008 07:30 AM

Anders Lybecker

Minimizing the number of roundtrips to the database by joining and retrieving larger resultsets in the name of "performance" is a poor design decision.

Instead you should query multiple time for each roundtrip. ADO.Net supports the feature by the DataReader.NextResult(). NHibernate utilizes this via MultiQuery: www.hibernate.org/.../performance.html

Anders Lybecker

May 15. 2008 02:26 PM

Jakob Andersen

@Anders
So what you say is that it is always a bad design decision to retrieve larger resultsets, even if you infact need them?

What about this scenario, two tables Products(Fields: ProductID, Name, ...., CategoryID) and Categories(Fields: CategoryID, Name) with a many-to-one relation between the two. We need to present the names of categories in our user interface along with the products. You would create two queries returning two resultsets and lookup the categories in memory for each product based on its CategoryID?

In this case joining the two in my opinion is a very good design decision.

Jakob Andersen

May 15. 2008 08:50 PM

Anders Lybecker

You are twisting your own words and taking my comment out of context.

Your solution in the example with the Invoice and InvoiceLines is a poor design decision - in fact, I cannot think of a worse solution (if you do not deliberately want to screw things up). You end up retrieving way to much redundant data from the Invoice table.

Anders Lybecker

May 15. 2008 09:36 PM

Jakob Andersen

Sorry if you feel I take your comment out of context, but you say straight out "Minimizing the number of roundtrips to the database by joining and retrieving larger resultsets in the name of "performance" is a poor design decision."

Im speaking in general not the single case I present. And I stress in my post that whether it is sensible to join in these cases is very much dependent on the structure and data involved. The case presented with Invoice and InvoiceLines is an example of a case where joining would be a bad solution, thats why I explain the benifits of batch fetching and shortly hint at MultiCriteria and MultiQuery.

Jakob Andersen

May 16. 2008 07:53 AM

Anders Lybecker

No need to apologize – I know you wrote your comment to spike me Smile (BTW: Jakob Tikjøb and I are colleagues.)

It is exactly my point that you do not stress, your solution is terrible. Apparently my point was not clear at all.

Anders Lybecker

Comments are closed