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.  [ 3 posts ] 
Author Message
 Post subject: Flush updates all dirty objects not just the Saved ones?
PostPosted: Mon Dec 29, 2008 11:51 pm 
Newbie

Joined: Mon Dec 29, 2008 10:31 pm
Posts: 4
We have noticed that NHibernate is updating entities for which we have never called Save/SaveOrUpdate.

We first noticed the problem when nullable columns were mapped to a non-nullable value type. NHibernate is marking the entity as dirty simply by loading it and updating the database to mach the entity without ever having called save. It was easy enough to change the property to nullable in the class definition. But this is just a symptom of a larger problem:

When something fails we simply discard the entity reference and don't call SaveOrUpdate assuming that ONLY those objects for which we called SaveOrUpdate on (or is cascaded from another saveorupdate) would be written to the database. But as stated all dirty objects are being updated.

My initial assumption was that we had configured the session incorrectly. We had some issues initially with entities not being saved at all unless we explicitly called flush before closing the session so we added a Session.Flush() in EndRequest. After bit digging found that the transaction was not started so now we are using FlushMode.Commit and Transaction.Commit rather than explicitly calling Session.Flush but this has not solved the problem.

I googled the issue and seems a few other people have had issues with automatic dirty checking but I didn't really find a good solution. I read somewhere that I should evict those objects if I dont' want to save them but this hardly seems like a good solution. Whats the point of calling SaveOrUpdate if all dirty objects will be saved at flush regardless??

Confused, please tell me this is a configuration issue and not a "feature." We we using the binary version 2.1.0.1001 from NHibernate.Linq project, perhaps a bug?


Thanks,
Kurt


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 30, 2008 2:52 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
Not, it's a feature, not a bug. session.Save/SaveOrUpdate are for making transient objects persistent. All objects that are in the session will be persisted anyway. That's the way hibernate works.

I'm still working with 1.2, but I think starting with 2.0 there should be a stateless session which can be used for loading objects without dirty checking.

_________________
--Wolfgang


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 30, 2008 2:21 pm 
Newbie

Joined: Mon Dec 29, 2008 10:31 pm
Posts: 4
Thanks for the quick reply.
I get that you need to tell NHibernate to make the object persistent with Save method which makes sence.

What we want to do is create a unit of work and anything added to the unit of work will be inserted or updated, if not explicitly added to the unit of work it will should be discared during commit. I thought this was the role of the session and my assumption was that the update method marked the object as dirty (or potentially dirty) and SaveOrUpdate was simply if IsTransient Save() else Update() as the method name implies.

Forgive my ignorance, but the actual behavior is significantly different than my expectations, with several potentially problematic ramifications. It appears I have completely misunderstood the role of the session and the update method. I'm no longer entirely sure what the correct usage of NHibernate is within a .net web application.

We currently open the session and transaction on begin request and commit the transaction on endrequest unless an error occured. This allows view commonents and helper methods to render navigation, kpi, and other content using the context bound session. Although navigation items are not usually modified by a request, some pages do allow users to modify navigation items so so we can't simply say load all items except what we intend to modify as readonly because the repository loading the data currently doesn't know or care if the data will be need to be modified.

This most straight forward might seem to be "don't modify the object then." However this does't work well either as a page might make multiple modifications. Several events are initiated during a post which may are processed by different listeners to determine if a change needs to be made to the object, in some cases one object might be modified but enter an invalid state so althought the memory version of the object was modified the object should NOT saved, but this is a graceful failure in which the user will be notified and/or the object added to an error queue. The transaction itself is not rolledback and other modifications may still be made. However what you are saying seems to indicate that I must rollback the transaction or evict the object.

This could potentially be a serious issue because if what your saying is by design the object will still be saved even though it the object is in an invalid state, nor can the transaction be rolled back (in some cases) because other modifications will be made. So now I'm not entirely sure what the best practice is?

I can partly understand why it works the way that it does since NHibernate returns the same instance an updated object that is not evicted could be queried again later returning the dirty object rather than a clean object, so evicting it seems like a good idea but if I were implementing this with POCO I would have the repository return a copy of the stored data and once I'm satisfied with my modifications I call Save updating the persisted object (perhaps this is the purpose of StatelessSesison).

There are a few potential solutions I can think of:

1) Perform all modifications within a try { .. if(valid) Save(obj) else Evict(obj) } catch { Evict(obj) }. I have no idea if evict cascades and would require us to manually track clean objects for eviction rather than dirty objects which seems complete backwards and not even sure how to determine which object may have been lazy loaded.

2) I discovered ILifecycle interface last night which could allow us to implement a our own Unit of Work by returning Veto OnSave unless we called Save explicitly. This seems like it might be doable. The potential issue here is that we would loose the implicit cascade functionality. The other issue is that since NHibernate returns the same object reference an object which was loaded, modfied but not saved could be loaded again later (and assumed clean) but was already invalid.

3) StatelessSesison. Sounded like the stateless session was intended for bulk loading so I didn't pay much attention to it. Session without cache I guess. But since part of the problem seems to be that NHibernate returns same reference rather than copy of object until save perhaps stateless session behaves more like I was expecting the session to behave.

4) Open readonly (Flush.Never should be readonly right?) session in BeginRequest to allow view components, helpers, etc to render whatever. The event listeners however would open a sperate session and transaction. The concern here is that we need to ensure all entities loaded from the readonly session loaded into the new session. This might have advantages over 3 since it would support caching and obect reference equality tests. It seems that SaveOrUpdateCopy might be my lifesaver here and I'm thinking this might be the best approach/recommended practice?

Thoughts?

Thanks,
Kurt


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.