Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,546
|
Comments: 51,161
Privacy Policy · Terms
filter by tags archive
time to read 3 min | 541 words

Originally posted at 11/18/2010

I got an interesting question today (I am teaching my NHibernate course now).

The tabular structure is similar to this:

image

But the desired object structure is:

image

That is quite different than the tabular model, but it is actually very easy to handle this with NHibernate.

Here are the mapping for the Address entity. We use the <join/> tag to have an entity that spans more than a single table:

<class name="Address"
       table="Addresses">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="City" />

  <join table="PeopleAddresses" >
    <key column="AddressId"/>
    <property name="IsDefault"/>
    <property name="ValidFrom"/>
    <property name="ValidTo"/>
  </join>

</class>

We then map the Person, using standard many-to-many mapping for the addresses:

 <class name="Person"
             table="People">

   <id name="Id">
     <generator class="identity"/>
   </id>
   <property name="Name" />

   <bag name="Addresses" table="PeopleAddresses" inverse="true">
     <key column="PersonId"/>
     <many-to-many class="Address" column="AddressId"/>
   </bag>
   
 </class>

There is just one thing thing to be aware of, you can’t add new addresses via the Person.Addresses collection, because the PeopleAddresses table has more data in it than just the keys. Presumably, you are handling this in some other fashion already.

All in all, this is a pretty elegant solution.

time to read 6 min | 1122 words

Note: I am not feeling very well for the past week or so, which is why I am posting so rarely.

NHibernate is meant to be used in an OLTP system, as such, it is usually used in cases where we want to load a relatively small amount of data from the database, work with it and save it back. For reporting scenarios, there are better alternatives, usually (and before you ask, any reporting package will do. Right tool for the job, etc).

But there are cases where you want to do use NHibernate in reporting scenarios nonetheless. Maybe because the reporting requirements aren’t enough to justify going to a separate tool, or because you want to use what you already know. It is in those cases where you tend to run into problems, because you violate the assumptions that were made while building NHibernate.

Let us imagine the following use case, we want to print a list of book names to the user:

using (ISession s = OpenSession())
{
    var books = s.CreateQuery("from Book")
        .List<Book>();

    foreach (var book in books)
    {
        Console.WriteLine(book.Name);
    }
}

There are several problems here:

  • We query on a large result set without a limit clause.
  • We read a lot of data into memory.
  • We only start processing the data after it was completely read from the database.

What I would like to see is something like this:

while(dataReader.Read())
{
     Console.WriteLine(dataReader.GetString("Name"));
}

This still suffer from the problem of reading a large result set, but we will consider this a part of our requirements, so we’ll just have to live with it. The data reader code has two major advantages, it uses very little memory, and we can start processing the data as soon as the first row loads from the database.

How can we replicate that with NHibernate?

Well, as usual with NHibernate, it is only a matter of finding the right extension point. In this case, the List method on the query also has an overload that accepts an IList parameter:

image

That make it as simple as implementing our own IList implementation:

public class ActionableList<T> : IList
{
    private Action<T> action;

    public ActionableList(Action<T> action)
    {
        this.action = action;
    }

    public int Add(object value)
    {
        action((T)value);
        return -1;
    }

    public bool Contains(object value)
    {
        throw new NotImplementedException();
    }

    // ...
}

And now we can call it:

using (ISession s = OpenSession())
{
    var books = new ActionableList<Book>(book => Console.WriteLine(book.Name));
    s.CreateQuery("from Book")
        .List(books);

}

This will have the exact same effect as the pervious NHibernate code, but it will start printing the results as soon as the first result loads from the database. We still have the problem of memory consumption, though. The session will keep track of all the loaded objects, and if we load a lot of data, it will eventually blow out with an out of memory exception.

Luckily, NHibernate has a ready made solution for this, the stateless session. The code now looks like this:

using (IStatelessSession s = sessionFactory.OpenStatelessSession())
{
    var books = new ActionableList<Book>(book => Console.WriteLine(book.Name));
    s.CreateQuery("from Book")
        .List(books);

}

The stateless session, unlike the normal NHibernate session, doesn’t keep track of loaded objects, so the code here and the data reader code are essentially the same thing.

time to read 1 min | 171 words

Why do we have to do something like that?
var blogs = s.CreateCriteria<Blog>()
    .Add(Restrictions.Eq("Title", "Ayende @ Rahien"))
    .List<Blog>();

We have to specify Blog twice, isn’t that redundant?

Yes and no. The problem is that we can’t assume that we are going to return instances of Blog. For example:

var blogs = s.CreateCriteria<Blog>()
    .Add(Restrictions.Eq("Title", "Ayende @ Rahien"))
    .SetProjection(Projections.Id())
    .List<int>();
time to read 1 min | 161 words

Before I start, I wanted to explain that NHibernate fully support the identity generator, and you can work with it easily and without pain.

There are, however, implications of using the identity generator in your system. Tuna does a great job in detailing them. The most common issue that you’ll run into is that identity breaks the notion of unit of work. When we use an identity, we have to insert the value to the database as soon as we get it, instead of deferring to a later time. It also render batching useless.

And, just to put some additional icing on the cake. On SQL 2005 and SQL 2008, identity is broken.

I know that “select ain’t broken” most of the time, but this time, it appears it does :-)

We strongly recommend using some other generator strategy, such as guid.comb (similar to new sequential id) or HiLo (which also generates human readable values).

time to read 7 min | 1352 words

System.DateTime is a value type in .Net, which means that it can never be null. But what happens when you have a nullable date time in the database, and you load it into a DateTime type?

Consider this simple example. Mapping:

<property name="UpdatedDate" nullable="True"  column="updated_date" type="DateTime" />

Property:

public DateTime UpdatedDate 
{
      
get { return m_dateTime; }
     
set { m_DateTime = value; }
}

When NHibernate loads a null value from the database, it cannot put a null in the UpdatedDate property, the CLR doesn't allow it. What happens is that the UpdatedDate property is set to the default DateTime value, in this case: 01/01/0001.

This can cause two major issues down the road. The first is that 01/01/0001 is not a valid date in SQL Server, so when you try to save the value, it will throw an exception. The second is that because of this issue, when NHibernate needs to track changes, it will check if null != 01/01/0001, and it will turn out that yes, this entity (which we never touched) has changed.

This can cause an extra update (and thus an exception) which can cause some fairly significant head scratching. The solution is simple, you need to let NHibernate know what to do with null values. This can be done by simply using a nullable type, such as:

public DateTime? UpdatedDate 
{
      
get { return m_dateTime; }
     
set { m_DateTime = value; }
}

Or by using the Nullables.dll library for 1.1, and specifying the correct type in the mapping:

<property name="UpdatedDate" column="updated_date" type="Nullables.NHibernate.NullableDateTimeType, Nullables.NHibernate" />

With this property:

public Nullables.NullableDateTime UpdatedDate 
{
   
get { return m_dateTime; }
   set { m_DateTime = value; }
}

Thanks for Sheraz Khan, for finding out and brining this to my attention, since then, I have run into the issue a couple of times, and I hope that if I blog about it, I will remember to match nullabilities.

FUTURE POSTS

  1. Partial writes, IO_Uring and safety - about one day from now
  2. Configuration values & Escape hatches - 5 days from now
  3. What happens when a sparse file allocation fails? - 7 days from now
  4. NTFS has an emergency stash of disk space - 9 days from now
  5. Challenge: Giving file system developer ulcer - 12 days from now

And 4 more posts are pending...

There are posts all the way to Feb 17, 2025

RECENT SERIES

  1. Challenge (77):
    20 Jan 2025 - What does this code do?
  2. Answer (13):
    22 Jan 2025 - What does this code do?
  3. Production post-mortem (2):
    17 Jan 2025 - Inspecting ourselves to death
  4. Performance discovery (2):
    10 Jan 2025 - IOPS vs. IOPS
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}