2

I'm having a two-tier (non-web) system, where I will load some objects into system memory, and flush them back to database occasionally. Let's assume there's just one object I need to update, but the object is a pretty big graph containing maybe hundreds of entities, and I use cascade to make the save propagate to the whole graph.

When Session.Update(the_big_object) is called in the second session, NHibernate "correctly" overrides every single entity in the whole object graph to the database, creating lots of updates, even if they are not changed. It's understandable because it has no idea what was changed outside the session. Using Session.Merge(the_big_object) does not help much since it would require a lot of queries as well.

The problem is in my case, usually there are only a few entities in the big graph that are actually changed. What I'm thinking is that, instead of using cascade to save the whole object, maybe it's a good idea to keep a dirty entity collection in the memory, and only flush those when needed. In order to do so I might need to add a dirty flag to those classes binding to their setters and so on.

Then I figured, isn't it just what NHibernate has been doing inside a session, to determine which objects are dirty? All these proxy / version things work perfectly for persistent objects, but not detached objects (or do they?). I just feel kinda dumb needing to do this again by hand.

Are there any suggestions / approaches I can take, or is there any magic trick I'm missing?

Thanks a lot!

Jerry Tang
  • 31
  • 4

3 Answers3

2

It doesn't look like there is a way of doing what you are asking unless you manually track the changed entities yourself. Take a look at this article. It's similar to what you are asking:

What is the best approach to update only changed properties in NHibernate when session detached?

Edit:

Can you reattach your object to a session before you make the updates? If so you could do something like this:

using(var session = sessionFactory.OpenSession())
    using(var tx = session.BeginTransaction()) 
    {
        var p = new Person {PersonId = 1};
        session.Lock(p, LockMode.None); // <-- This is the secret sauce!
        p.Firstname = "Bob";
        // No need to call session.Update(p) since p is already associated with the session.
        tx.Commit();
    }

Above code taken from: NHibernate - flagging specific properties as 'dirty'

Community
  • 1
  • 1
Cole W
  • 15,123
  • 6
  • 51
  • 85
  • Thanks for the answer! I actually read the article you provided - but it's not entirely the same because I don't really mind it updates to unchanged fields/columns - it's still the same number of updates. I'm more concerned about unnecessary update sql commands (like only 3 entities changed but 100 updates are fired). As to the example you provided, logically it would work for sure, the reason I'm not too up for it is because the entity might get tweaked all the time, and I'm concerned that it would cause performance issues. – Jerry Tang Dec 29 '11 at 17:01
  • I further read the article "flagging specific properties as 'dirty'", the use case scenario is a bit different - the change seems to be one time only, so Session.Lock() makes sense there. – Jerry Tang Dec 29 '11 at 17:17
0

A couple of untested ideas:

  • Load the entire object graph in the 2nd session using a query or by accessing any collections that would be lazy loaded. Then merge the detached object into the 2nd session; this should not require any additional queries because the entire object graph will be in the cache.

  • Implement INotifyPropertyChanged in your objects and track of dirty objects in the top level object. This is a fair amount of work if you have many different types in the graph.


Another option may be to serialize the original session and deserialize when you are ready to flush the session. You should be able to Merge the entity back into the original session without an additional database trip.

Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
  • Thanks for the answer! I observed the behavior by Session.Merge(), and it issues pretty much the same queries as if I manually query them. Since I don't need that "unsaved state" in other places, the total amount of queries remains the same. INotifyPropertyChanged is interesting though, but yeah despite the defined Interface, the whole thing is still pretty hard coded.... – Jerry Tang Dec 29 '11 at 17:06
0

Just to have a clue:

Think of storing the original object in memory( e.g. Session for web apps) and finding out the changeset in when the updated object comes back (e.g in postback ).

I'm looking for a method to attach the deattached object to a session without querying DB.

Jahan Zinedine
  • 14,616
  • 5
  • 46
  • 70
  • Pretty much what I was asking - but outside session scope I don't think hibernate can know what's changed without querying again, or brute force override... So I'm just wondering if there's any way to deal with the issue more elegantly – Jerry Tang Dec 29 '11 at 17:09