These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Updating node cascades unnecessarily to subtree
PostPosted: Wed Feb 27, 2008 7:47 am 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
I'm mapping a tree data-structure as follows:
Code:
<class name="Category" lazy="false">
   <id name="ID"><generator class="native"/></id>
   <property name="Title" not-null="true"/>
   <set name="SubCategories" inverse="true" lazy="false" cascade="all-delete-orphan">
      <key column="ParentCategoryID"/>
      <one-to-many class="Category"/>
   </set>
   <many-to-one name="ParentCategory" column="ParentCategoryID"/>
</class>

The cascade="all-delete-orphan" seems appropriate since this is a typical case of aggregation. It does the work I need it to except for one problem. When a node's title alone is updated, and then I call
Code:
session.Update( node );
the entire subtree gets updated to the database even though no other part of it has changed. This is unnecessary and inefficient, especially when editing a node with a large subtree. Can I turn off the cascade for a particular update operation so that these unnecessary updates do not occur? Or is there a more recommended approach to use in this kind of situation?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 27, 2008 8:29 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
I'm going to ignore your real question for a moment, and ask you why you are calling session.Update(node). Is this object already associated with a session? If so just call Flush().

Update is intended to associate a transient object which already exists in your database with an nhibernate session and state that the object has changed. You have multiple options in how this update process happens. Either it can always perform an update (which I believe is the default and similar to the behavior you are seeing) or it can perform a select, compare the differences for every item and only update if something changed. Either way you're stuck with a lot of inefficiencies in your scenario.

If your object is not associate with a session, I would suggest loading your object you want to change first, making the modification you want and then flushing the session. That way only the changed records will need to be persisted.

If these suggestions don't help providing some more information about your application architecture may help us determine a better way to solve your issue.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 27, 2008 10:22 am 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
The entire tree is loaded in one session which is then closed. When a node's title is changed, a new session is opened for the specific purpose of persisting the title change. So is Update() appropriate to this scenario?

jchapman wrote:
If your object is not associate with a session, I would suggest loading your object you want to change first, making the modification you want and then flushing the session. That way only the changed records will need to be persisted.

I tried your suggestion with the following code:
Code:
session = SessionFactory.OpenSession();
Category c = session.Get<Category>( editedCategory.ID );
c.Title = editedCategory.Title;
session.Flush();
session.Close();
Is this what you had in mind? There's only one UPDATE that occurs, as you said, but wow there sure are a lot of SELECT statements. It doesn't look to me to be more efficient this way. What do you think?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 27, 2008 10:44 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
Yes, this in fact what I meant. The selects are also controllable through configuration.

You have options of using lazy loading for your collections and many-to-one properties. You also have the choice of loading many-to-one properties via left/inner joins to only use a single select statement. You also have the option of eagerly loading a collection with an outer join when the main entity is loaded.

Obviously there are pluses and minuses to each option. Let me know if you don't know how to configure these settings. I could show some examples. They are also listed in the documentation.

By the way, select statements are nowhere near as costly as update statements, so just by itself I would say this is an improvement. Of course you're probably duplicating the selects between the initial load and the update in your real production application.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 27, 2008 11:16 pm 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
This might be a good time to ask for advice about architecture. My application is a desktop app, and it uses SQL Server Compact Edition as the database. I haven't yet put a lot of thought into the persistence architecture, instead just using the approach most obvious to me. That is, a new session is opened for each interaction with the database. However, I wonder whether or not that's the best approach. Would it be better to keep a single session open for the entire time the app is running? Would the above-mentioned problem be solved in that case? Is there any danger the session could unexpectedly close before the user exits the app?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 28, 2008 8:36 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
To be fair desktop applications are not really my specialty, I mostly deal with web applications. That being said I still believe in the approach that sessions are as short lived as possible.

There are many ways to deal with the session life cycle, but having one open session for the entire application seems like a mistake. Also keep in mind that the performance of a session will deteriorate over time as more and more entities are loaded into it. If you don't evict the items yourself or close the session the Flush method will need to compare the state of all objects it currently holds in session. This will also stop the garbage collector from clearing these objects since the session still holds a reference to them, and you own a reference to the session.

For a desktop application I would take one of two approaches. One, I would create a new session per form. So when I load a new form, start a new session to load my data, perform actions with those objects on the form, then when they are finished with the form flush the changes to the object. You're session could theoretically be open for a long period of time, but ideally it will be relatively short.

You could also use a DTO approach where you transfer data objects to the UI. You load your business objects in a new session, apply the data to the DTO objects, and then pass that data to the UI. When the UI is finished with operations it could rely on a single method in a service layer to apply the changes. That service layer would create a new session when the method is first received, perform all operations it received from the UI and then flush and close the session upon exit from the service layer.

I think both of those are solid starters, some others on this forum may have some differing opinions though.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 28, 2008 11:52 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
Another thing I thought of would be to use the Session's disconnect features. This way you can have a session open for a long time without worrying about the impact on the associated connection to the database. This way you could use the session to track just what changed, and when it comes time to flush the changes, reconnect the session, flush the changes and then disconnect the session again (or possibly even close it if you're done with it).

This approach would work well whether the session lives over the lifetime of the form or the application. It certainly seems ideal for any lifetime dependent on a user.

For more information see the docs:

http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/#transactions-disconnection[/url]


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 12, 2008 11:37 am 
Beginner
Beginner

Joined: Fri Sep 28, 2007 9:50 pm
Posts: 34
The suggestion in your last post I think is called “session-per-application”. I read that this is a bad idea since the session’s cache will grow continuously throughout the life of the application. There are ways of dealing with that, but after finally doing some long overdue background reading in the Hibernate texts, I’ve found a much simpler solution to the original problem. That is, I just call session.Lock() on each of the node’s children. That way NH knows not to cascade the update. Wow, that was simple! I’m doing to keep reading those Hibernate books.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.