Still on NHibernate...

Yesterday I was complaining about the way NHibernate persists entities that contain collection of components. After some discussions on the Forums, Karl suggested that I should call session.Flush after calling the Save(OrUpdate) method to force the generation of the collection of components maintained in the collection. This means that something along these lines should work:

  • Open a session
  • Begin transaction
  • Insert into db by calling the Save(OrUpdate) method
  • Flush the session by calling session.Flush
  • //perform other work here
  • Rollback or commit the transaction

Notice that I no longer call session.Flush after ending the transaction...After trying this approach, I can confirm that it does work. However, I still think that this is not intuitive and it really shouldn't work like this. When I persist an entity, I'm expecting that all of its depend objects are persisted too (persisted here means that the necessary insert commands are automatically generated and executed over the existing transaction). For instance, if I had to write SQL code to perform this operation, I'd do something like this:

  • start transaction
  • insert into main table
  • insert into secondary table (ie, insert the components)
  • //perform other work here (ie, write some code that tests the insertion)
  • rollback the transaction

I guess that the main problem here is that NHibernate has its own way of persisting entities to the database and, most of the time, everything works out as expected. Unfortunately, that is not the case when an entity has a collection of components. And even though I really don't know much about NHibernate, I really can't see how the default behavior is the correct one in this kind of scenarios...

Filed under:

Comments

# Eric Hauser said:

I only have experience with Java Hibernate, but I am assuming the APIs are the same.  If you want to change the default behavior of how that works, then you can just change the FlushMode on the Session.  I believe in your case it sounds like you want FlushMode.ALWAYS, so that the SQL is executed when you make the call to save.

Friday, September 21, 2007 7:04 AM
# luisabreu said:

Hello Eric.

I'm not sure if i'm esplaining myself correctly, but to me the problem is that I have an entity which is saved to the db by using an existing transaction. the main problem is that a save operation on this entity should affect 2 tables, and so i expected to find all the necessary insert statements inside the same transaction. unfortunately, i'm not seing this. for instance, in my scenario (it was a simple test), i had something like this:

begin tran

session.save(myentity)

roll back tran

ok, for this simple scenario, the logs showed something like this:

...

begining ado.net transaction

generating insert command for main table

rollback

what's the problem? well, in my opinion, the logs should have something like this:

...

begining ado.net transaction

generating insert command for main table

generating insert command for secondary table

rollback

in fact, that is what happens when you have an entity has a collection of other entities. but it does not happen when an entity has a collection of components! to get the behavior i want, i need to do something like this:

begin tran

session.save(myentity)

session.flush() ---> this generates the sql for the secondary table

roll back tran

again. i really know little about nhibernate, but if i'm wrong about this behavior, then i'm really ready to drop it and go back to writing sql...

Friday, September 21, 2007 8:51 AM
# Eric Hauser said:

Without defining a flush strategy, it is not guaranteed to work like the way you want it to.  If you look at Section 10.9 of the Hibernate docs (www.hibernate.org/.../objectstate.html- again I am assuming that NHibernate works the same) it states:

"Except when you explicity flush(), there are absolutely no guarantees about when the Session executes the JDBC calls, only the order in which they are executed."

You pretty much have to call Session.flush() or commit the transaction to guarantee that any SQL will get called.  It is not even guaranteed that the SQL for your entity gets executed when you call save(), it gets happens to work like that.

Hibernate takes a little getting used to, but my experiences by far have been more positive than negative.  Especially when you are working with an existing schema and generate your mappings and validations from it.  Good luck =).

Friday, September 21, 2007 11:32 AM
# luisabreu said:

Hello again Eric.

hum...yeah, i understand what you're saying. now, the problem is that something like this (even though it's completly wrong):

begin tran

session.save

rollback tran

session.flush

should never never persist the items of the collection maintained by the entity in the db table (and in nhibernate - not sure if that is the case with hibernate) it happens. that's why i'm so pissed with it. as i've said before, if you have a collection of entities (instead of components) it works as it should...but again, maybe next wekk i'm feeling alittle better :)

thanks again for  helping me!

Friday, September 21, 2007 3:41 PM
# Mike said:

I think the problem is really more one of sematics. It was confusing to me at first to call 'save' and then not actually have a persisted object. What is important to remember with NHibernate is its concept of a Unit of Work, so that when you call 'save' you are actually saving it to the current Unit of Work, which will be flushed (synched with the db state) either transparently or manually by your application. Once you start to work within the context of a Unit of Work/Identity Map things with NHibernate start to clear up.

Mike

Sunday, September 23, 2007 11:38 PM
# luisabreu said:

Hello Mike.

I still think that i'm not explaining myself correctly here...if you run the sample i'm talking about and perform the session.flush after the rollback, you'll end up with the wrong result. to me the problem is that if you're generating the insert instruction for the main entity, then you must also generate the other inserts since you must garantee that they are all commited or aborted. this isn't really the case when you're using nhibernate.

Monday, September 24, 2007 3:27 AM

Leave a Comment

(required) 
(required) 
(optional)
(required)