25

Does batch update command exist in NHibernate? As far as I am aware it doesn't. So what's the best way to handle this situation? I would like to do the following:

  1. Fetch a list of objects ( let's call them a list of users, List<User> ) from the database
  2. Change the properties of those objects, ( Users.Foreach(User=>User.Country="Antartica")
  3. Update each item back individually ( Users.Foreach(User=>NHibernate.Session.Update(User)).
  4. Call Session.Flush to update the database.

Is this a good approach? Will this resulted in a lot of round trip between my code and the database?

What do you think? Or is there a more elegant solution?

Graviton
  • 81,782
  • 146
  • 424
  • 602

6 Answers6

42

I know I'm late to the party on this, but thought you may like to know this is now possible using HQL in NHibernate 2.1+

session.CreateQuery(@"update Users set Country = 'Antarctica'")
.ExecuteUpdate();
MPritchard
  • 7,031
  • 7
  • 28
  • 40
  • Would recommend marisks's answer for anyone using NH 3.2+ – MPritchard Apr 10 '13 at 08:33
  • 1
    This only works if the property for all `Users` would be set to the same value `Antartica`. But if a `List` is batch updated with all different counties, this `HQL` isn't going to to the job. Though it is a solution to the particular exemplary situation of the OP. – Mike de Klerk Dec 20 '13 at 08:03
21

Starting NHibernate 3.2 batch jobs have improvements which minimizes database roundtrips. More information can be found on HunabKu blog. Here is example from it - these batch updates do only 6 roundtrips:

using (ISession s = OpenSession())
using (s.BeginTransaction())
{
    for (int i = 0; i < 12; i++)
    {
        var user = new User {UserName = "user-" + i};
        var group = new Group {Name = "group-" + i};
        s.Save(user);
        s.Save(group);
        user.AddMembership(group);
    }
    s.Transaction.Commit();
}
marisks
  • 1,812
  • 1
  • 17
  • 33
9

Starting with NHibernate 5.0 it is possible to make bulk operations using LINQ.

session.Query<Cat>()
.Where(c => c.BodyWeight > 20)
.Update(c => new { BodyWeight = c.BodyWeight / 2 });

NHibernate will generate a single "update" sql query.

See Updating entities

bN_
  • 772
  • 14
  • 20
9

You can set the batch size for updates in the nhibernate config file.

<property name="hibernate.adonet.batch_size">16</property>

And you don't need to call Session.Update(User) there - just flush or commit a transaction and NHibernate will handle things for you.

EDIT: I was going to post a link to the relevant section of the nhibernate docs but the site is down - here's an old post from Ayende on the subject:

As to whether the use of NHibernate (or any ORM) here is a good approach, it depends on the context. If you are doing a one-off update of every row in a large table with a single value (like setting all users to the country 'Antarctica' (which is a continent, not a country by the way!), then you should probably use a sql UPDATE statement. If you are going to be updating several records at once with a country as part of your business logic in the general usage of your application, then using an ORM could be a more sensible method. This depends on the number of rows you are updating each time.

Perhaps the most sensible option here if you are not sure is to tweak the batch_size option in NHibernate and see how that works out. If the performance of the system is not acceptable then you might look at implementing a straight sql UPDATE statement in your code.

Steve Willcock
  • 26,111
  • 4
  • 43
  • 40
4

You don't need to update, nor flush:

IList<User> users = session.CreateQuery (...).List<User>;
users.Foreach(u=>u.Country="Antartica")
session.Transaction.Commit();

I think NHibernate writes a batch for all the changes.

The problem is, that your users need to be loaded into memory. If it gets a problem, you can still use native SQL using NHibernate. But until you didn't prove that it is a performance problem, stick with the nice solution.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • 1
    This depends on the FlushMode. session.FlushMode = FlushMode.Never won't save without an explicit call to Flush(). Your code would work with FlushMode.Commit however – MPritchard Feb 18 '10 at 07:25
  • @Stefan I am not able to get batch updates to work... can you please help http://stackoverflow.com/questions/26471952/nhibernate-executing-updates-in-batches – harishr Oct 21 '14 at 07:05
0

No it's not a good approach!

Native SQL is many times better for this sort of update.

UPDATE USERS SET COUNTRY = 'Antartica';

Just could not be simpler and the database engine will process this one hundred times more efficiently than row at a time Java code.

duffymo
  • 305,152
  • 44
  • 369
  • 561
James Anderson
  • 27,109
  • 7
  • 50
  • 78
  • 1
    He's using NHibernate, that's .NET not Java :) – Steve Willcock Apr 23 '09 at 10:05
  • 1
    I am confused here: if your argument is valid then there should be no use of ORM because db engine will always process SQL code many times more efficiently than ORM code... am I right? – Graviton Apr 23 '09 at 11:12
  • Ngu, I have updated my answer to respond to your comment there. – Steve Willcock Apr 23 '09 at 12:11
  • Well ORM mapping is great for single object CRUD access to a database, you lose significant amounts of power and flexibility by not using native SQL. – James Anderson Apr 24 '09 at 06:06
  • 6
    See Martin's answer, you still can use your ORM, no need to steal from your employer and write ado.net code. – mxmissile Feb 17 '10 at 14:15
  • 1
    @Graviton There is no such thing as a free lunch; the performance of database related tasks is traded for the ease of coding by using an ORM. – Mike de Klerk Dec 20 '13 at 08:06
  • Indeed I would agree to do a SQL update in this case. 6 roundtrips (example above) vs 1? It's obvious the answer! At least keep it within the model layer (so you know is all in the same place). – Kat Lim Ruiz Mar 30 '15 at 03:08
  • what if my user says he lives in '; DROP TABLE USERS; -- robert was here any ORM will deal with this case for you. – increddibelly Oct 17 '16 at 09:03