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,627
|
Comments: 51,250
Privacy Policy · Terms
filter by tags archive
time to read 45 min | 8877 words

No, really for real this time. I opened my mailbox today to find a very interesting message from the NHibernate commit logs:

NH-258 - Custom SQL support (including stored procedures)

It may look fairly innocent comment, but it has quite an impact on several nay-sayers that will clutch stored procedures until you pry them out of their cold dead hands. Also, sometimes Stored Procedures (or raw SQL) are useful :-).

So, armed with nothing but a toothpick and some shoes' polish, I turned to the task of finding out how to use this wonderous new feature. I started on the Hibernate documentation in the subject

The table model is very simple:

(Image from clipboard).png

I then create a simple set of stored procedures, all of which were fairly standard, like this one:

ALTER PROCEDURE [dbo].[EmployeesInsert](

      @Name nvarchar(50), @Surname nvarchar(50),

      @Email nvarchar(50), @Type int, @Id int

)

AS

      INSERT INTO [Employees]

      (

            [Id], [Name], [Surname], [Email], [Type]

      )

      VALUES

      (

            @Id, @Name, @Surname, @Email, @Type

      )

 

      RETURN @@Error

I have created those procedures via code generations, but I think that you'll agree that most existing stored procedures will have a similar look. I have the following SPs:

·         EmployeesInsert

·         EmployeesUpdate

·         EmployeesDelete

·         SalariesInsert

·         SalariesUpdate

 ·     SalariesDelete

The only slightly strange thing about these procedures is the position of the ID parameter. While in most SP it is the first parameter, it seems that NHibernate requires it to be the last parameter on the stored procedure. Also note that specifying SET NOCOUNT ON will cause NHibernate to think that it didn't manage to save the row, and throw. The SP must return a count of 1.

So, given those, how am I going to proceed?

First thing that I should do it to update the XSD schemas, this is so I could get intellisense inside VS.Net when I edit the mapping files. Then, let us create our entites:

(Image from clipboard).png

This is a fairly simple model to grasp, but hard to implement. I'll not show you everything, but I'll at least put you on the right path. Let us start with mapping the Employee class:

<class name="Employee"

         table="Employees"

         discriminator-value="1"

         lazy="true">

      <id name="Id"

            unsaved-value="0">

            <generator class="assigned"/>

      </id>

      <discriminator column="Type"/>

      <property name="Name"/>

      <property name="Surname"/>

      <property name="Email"/>

      <set name="Salaries"

             generic="true">

            <key column="EmployeeId"/>

            <one-to-many class="Salary"/>

            <loader query-ref="allSalariesForEmployee"/>

      </set>

 

      <subclass name="Manager"

                    discriminator-value="2">

            <sql-insert>

                  exec EmployeesInsert ?,?,?,2,?

            </sql-insert>

      </subclass>

 

      <sql-insert>

            exec EmployeesInsert ?,?,?,1,?

      </sql-insert>

</class>

This is mostly standard NHibernate mapping, with some interesting additions, notice the generic="true" in the set defination? That means that you can use ISet<T> without using NHibernate.Generics.

Now, for now, let us ignore the <loader> in the set element, and look at the bottom of the code, where we find a <sql-insert> statement, which directs NHibernate to use the text inside when it is inserting an entity to the database. A couple of points about the question marks before we continue:

  • The identifier of this entity is always the last one.
  • The rest of the fields appear in the order that they were defined in the XML.
  • The last two statements are both wrong, in order to know, you need to check the generated SQL that NHibernate generates, and only then you will know. The issue here is that it gets quite complex with inheritance hierarcies, multi column fields, etc.

NHibernate transforms this call to:

exec

EmployeesInsert @p0,@p1,@p2,1,@p3

As I have no real control on the order of the parameters, my SP needs to be changed approprirately (above it appears after the changes). Now, about managers, you noticed that I specified a different statement there? That allows me to pass the different discriminator value (or maybe call ManagersInsert), etc.

Before we get to the set mystery, let us take a look at how I mapped Salary:

<class name="Salary"

         discriminator-value="-1">

      <id name="Id">

            <generator class="assigned"/>

      </id>

      <discriminator column="Type"/>

      <property name="Remark"/>

      <many-to-one name="Employee"

                         class="Employee"

                         column="EmployeeId"/>

      <subclass name="HourlySalary"

                    discriminator-value="1">

            <property name="HourlyRate"

                          column="ParameterOne"/>

            <sql-insert>

                  exec SalariesInsert ?,?,?,1,?

            </sql-insert>

      </subclass>

      <subclass name="GlobalSalary"

                    discriminator-value="2">

            <property name="TotalSalary"

                          column="ParameterOne"/>

            <sql-insert>

                  exec SalariesInsert ?,?,?,2,?

            </sql-insert>

      </subclass>

 

      <sql-insert>

            raiserror ('Salary is an abstract type, can''t insert it!',10,1)

      </sql-insert>

</class>

This is not very different, isn't it? Notice that because salary is an abstract type, I raise an error if an attempt is made to save it (not really neccecary, since this is not possible, but whatever). Then, each subclass defined each own insert statement.

One thing to notice here is the parameter orderring. It appears that in this case the orderring is in the order defined in the mapping, in reverse hierarchal depth. Which means that the members of the subclass appear first, and then the members of the parent class, and last (but not least) the id.

For reference, here is the SalariesInsert procedure:

ALTER PROCEDURE [dbo].[SalariesInsert]

(

      @ParameterOne int, @Remark nvarchar(50),

      @EmployeeId int, @Type int, @Id int

)

AS

      INSERT INTO [Salaries]

      (

            [Id], [Type], [ParameterOne], [Remark], [EmployeeId]

      )

      VALUES

      (

            @Id, @Type, @ParameterOne, @Remark, @EmployeeId

      )

 

      RETURN @@Error

Now, let us check that facinating association between employees and salaries:

<loader query-ref="allSalariesForEmployee"/>

This means that when NHibernate wants to load the employees' salaries, it needs to execute the query (not SP) that is specified. In this case, here is the query:

<sql-query name="allSalariesForEmployee">

      <load-collection alias="salaries"

                               role ="Employee.Salaries"/>

      exec [SalariesByEmployeeId] ?

</sql-query>

Note that we need to specify which collection this query resolves. The SalariesByEmployeeId is simply:

ALTER PROCEDURE [dbo].[SalariesByEmployeeId]

(

      @EmployeeId int

)

AS

      SELECT

            [Id], [Type], [ParameterOne], [Remark], [EmployeeId]

      FROM [Salaries]

      WHERE       [EmployeeId] = @EmployeeId

So, that is all that I have to say so far, you can check Hibernate's documentation for all of the details, and check out the latest NHibernate code to play with it.

This is probably not going to help you map directly to pre-existing stored procedures, but it make it that much easier to work in those envrionment where you just can't get the data from the tables (for various reasons, not the least of which is the 160 columns table), or where Stored Procedures are mandated.

I'm very excited by this because I know that there are several places where this can be very helpful. I love PIVOT, and it was annoying not to be able to use it from NHibernate.

The disadvantages - You take away from NHibernate the ability to comprehend the structure of the tables, this mean that it can't do joins, eager fetching, etc. This is extremely important capability that just vanished. Likewise for queries, NHibernate's abilities to query your objects is being severly limited using this method of operation.

time to read 9 min | 1692 words

You may have noticed that I have create some wrappers around NHibernate, to make it easier to work with. NHibernate's API is very rich, and it can get pretty long if you are not careful. Consider the following:

Session.CreateCriteria(typeof(Post))

      .Add(Expression.Gt("PostedAt", DateTime.Now.AddDays(-3)))

      .Add(Expression.Eq("ShowInMainPage", true))

      .AddOrder(Order.Desc("PostedAt"))

      .SetFirstResult(0)

      .SetMaxResults(25)

      .List();

The above is very clear way, if long winded, to say "give me the last 25 posts in the last 3 days that should be shown in the main page". I swim around this API like fish in the water, until it comes to testing.

Trying to mock the above sentence is a PITA of the first order, and I say this as someone who wrote a mocking library :-)

Partly because of this, partly because I liked the API from Active Record, partly because I wanted a mode DDD model to work with, and partly because a client wanted to avoid commitment to a specific OR/M, I created a wrapper around it that gave some very nice features, adding code generation didn't hurt, so the above query now looks like this:

Repository<Post>.FindAll(0,25,Order.Desc("PostedAt"),

                         Where.Post.PostedAt.Gt(DateTime.Now.AddDays(-3)),

                         Where.Post.ShowInMainPage.Eq(true));

Using the Repository<T> is very simple, you can check the API here:

(Image from clipboard).png

Most of the saving are done by removing the fluent interface, and while I feel that this is simpler in most cases, there are some things that it just can't express.

I have talked before about cachable queries, in NHibernate, the are expressed by calling SetCachable(true), and setting the Cache Region, if neccecary. My API doesn't allow for it. Oh, I could add another parameter, but I already have a possible parameter explosion (check out the number of overloads on FindAll() ), and going this route would force me to add a parameter that would force bypassing the cache, etc.

Clearly, NHibernate's approach is much more reasonable now, it is easily extensible to support more options, and it doesn't break down when the complexity goes up.

I really like the fact that I have intellisense at my hands, and that I don't have to scroll through the 50+ methods (I counted) that some of the interfaces in NHibernate have. In addition to that, I have a problem where I want to cache a particular query, part of the time, and only for a very specific part of the time. In the part in question the queryis called roughly 3,650,000 times during the process of executing a certain web request [three points to the first fellow who can tell me what I'm doing]. Obviously going to the database per each query isn't going to be worth my time.

This query is sitting very low in the stack, and I really don't want to have to change / modify it in place, and I don't really like the idea of a caching version vs. non-caching version. After getting up and hitting the wall a couple of time with someone else head (What, you expected me to use mine? I use it for thinking. :-) ), I came up with the following solution:

(Image from clipboard).png

The usage is fairly simple:

using(With.QueryCache())

{

      ICollection<Post> postsToShow = Repository<Post>.FindAll(0, 25, Order.Desc("PostedAt"),

         Where.Post.PostedAt.Gt(DateTime.Now.AddDays(-3)),

         Where.Post.ShowInMainPage.Eq(true));

}

I'll be the first to admit that the "using(With ..." syntax is not the best there is, but in this case, I don't need to special case the exception case, so I don't think that I need to use a function that takes a delegate here.

The really interesting part is the TemporaryQueryCache(), which will enable the query cache for the duration of the using() statement. This means that I can selectively turn it on/off for a part of the code that require caching without actually modifying any code lower in the stack. This enables me to make much better decisions with regard to how and when I can use caching.

I commited my changes, but they are still in the proof of concept stage, and may change.

You can check them out here, or check out the entire tree using:

svn co http://svn.berlios.de/svnroot/repos/rhino-mocks/trunk/rhino-commons

time to read 1 min | 93 words

Life gets interesting with creative spelling error. I was checking a problem with a page today when I noticed the... interesting name it had... "EmployeeAssassinationValidatorRules.aspx"

No, I don't work on that kind of system.

The name should haved been named "EmployeeAssignationValidatorRules.aspx", or better yet, "EmployeeAssignmentsValidatorRules.aspx". But creative mispelling and taking the first result from Babylon spelling fixer.

I wonder what would happen if I didn't both to actually read the page name, how long it would go unnoticed...

time to read 5 min | 871 words

Well, I just added the last piece in what I consider the major features to Binsor, which is component references. I'm a big fan of decorators and chains of responsabilities, which mean that I tend to create a lot of references between objects.

In Binsor, it is as natural as this:

import Rhino.Commons

 

Component(defualt_repository, IRepository, NHRepository)

 

customer_repository = Component(customer_repository,

       IRepository of Customer, 

       CustomerValidationRepository of Customer)

#notice that I'm using @ here for compnent reference

customer_repository.inner = @defualt_repository

Just by defining the component, it is automagically expose it for references as @component_name. You can also define it in the Component itself as a compiled literal string, instead of a string. I can even use generic types there (which is a weakness in Boo), although not in a very nice syntax.

Update: The syntax weakness in generics in Boo was fixed (about 12 hours from the moment that I posted it), so the code above is very clean again. I know why I like OSS now!

With this done, I'm not in that blissful world where you got what you wanted and everything is fuzzy around the edges. I got three major things done in the last 12 hours, Windosr's IOC-29, Binsor (which required IOC-29, and NHibernate batching. I haven't done this amount of coding in what feels like ages.

Damn, that feels good.

The source can be found here

time to read 11 min | 2026 words

On the on-going battle between yours truly and XML, there has been a score on the good side!

I just finished implementing most of the usable functionality in Binsor, which is a Boo DSL* that is directed at configuring Windsor. Before I get into the details, take a look at the most minimal xml configuration possible for Windsor:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

       <components>

              <component id="default.repository"

                              service ="Rhino.Commons.IRepository`1, Rhino.Commons"

                              type="Rhino.Commons.NHRepository`1, Rhino.Commons"/>

       </components>

</configuration>

Now, let us take a look at the same configuration using Binsor:

import Rhino.Commons

Component("defualt_repository", IRepository, NHRepository)

This is it!

What is even more fun, is that you get to treat the component as if it was a real object, instead of a lump of configuration data that you need to work with. Take a look at how you do it for Windsor using XML here.

Take a look at how you do it using Binsor:

import Rhino.Commons

email = Component("email_sender", ISender, EmailSender)

email.Host = "example.dot.org"

It can't get any more natural than that.

Of course, the problem with the XML configuration for Windsor only start when you crazy with it and build an object graph of arbitrary depth no less than five. I stopped trying to make it work when I got 15 different XML files, mostly containing the same information.

Now, if I want to add the same component to listen for each of my server, I can simple do this:

import Rhino.Commons

 

servers = [   "server1",    "server2",    "server3"]

 

for server in servers:

       listerner = Component("listener_for_${server}", IListener, WSListener)

       listener.Server = server

If I want to make a change to the configuration, I can make it in one place, and all the listeners are updated.

Did I mention already that it is debuggable ?!

* Maybe DSL is taking it a bit too much, though. It is a single class that has some fancy sticks in it, and a lot of Boo magic.

time to read 3 min | 517 words

As of about 90 minutes ago, NHibernate has batching support. :-D

All the tests are green, but there may be things that broke in exciting ways, so I encourage you to try it out and see if you can break it. This functionality exists only for SQL Server, and only on .Net 2.0 (for complaints, go directly to the ADO.Net team).

You can enable this functionality by adding this to your hibernate configuration.

<

add key="hibernate.batch_size" value="10" />

Setting this size to very large number and treating NHibernate as an OO tool for bulk inserts is still not recommended.

My previous tests showed about 50% performance benefits over normal calls, I decided to try to take the new code for a speed using NHibernate's perf tests. They are fairly simple, but they are at least an indication of what is going on. The tests I run were all run against a local instance of SQL Server, with log level set to WARN. The test just compare similar operations using NHibernate and direct ADO.Net for some operations, usually inserts / deletes in increasing amounts. (For reference, I'm running the Simultanous() test from PerformanceTest fixture).

I should also mention that these are by no mean real benchmarks, it is more in the way of an indication.

With no batching:

(Image from clipboard).png

As you can see, there isn't much of a performance difference between the two, NHibernate has about 15% overhead, mostly it can be seen as a background noise, especially on the lower ranges.

Let us try with a batching of 25, shall we?

(Image from clipboard).png

Now the roles are reversed, and it is NHibernate that is faster. In fact, in this benchmark, it was on average faster by 25% - 30% than the direct ADO.Net code (without batching). Just for kicks, I run the benchmark with batch size of 256, and got about 30% - 45% improvements.

(Image from clipboard).png

All in all, I think that I like this :-D

As a side note, most of the performance in an ORM is not in the INSERT / UPDATE / DELETE side of things, but rather in how smart the engine in SELECTing the data. Issuing a thousands unnececary SELECTs is going to be a performance hog no matter what you do.
time to read 6 min | 1015 words

Another thing that came up in conversation today, a very good example of where the data set model breaks down. Take a look at the following (highly simplified) tables:

(Image from clipboard).png

Each type of employee gets a different salary calculation for overtime per the (current) salary type:

Global Hourly Global + Hourly
Manager None 15% over hourly rate 15% over hourly rate, to a max of totally 20% of monthly salary over a calendar month
Employee None 12% over hourly rate 12% precentage
Freelance 10$ hour 9% over hourly rate 10$ hour or 9% over hourly rate, lowest one

Given the above business rules, how do you propose to write the AddOverTime() method using the dataset model in a maintainable fashion? There will be additonal employee types and additional salary types in the future.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. Recording (20):
    05 Dec 2025 - Build AI that understands your business
  2. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  3. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  4. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
  5. RavenDB News (2):
    02 May 2025 - May 2025
View all series

Syndication

Main feed ... ...
Comments feed   ... ...
}