Sep 21

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…

7 comments so far

  1. Eric Hauser
    12:04 pm - 9-21-2007

    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.

  2. luisabreu
    1:51 pm - 9-21-2007

    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
    roll back tran

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

    begining transaction
    generating insert command for main table

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

    begining transaction
    generating insert command for main table
    generating insert command for secondary table

    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.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…

  3. Eric Hauser
    4:32 pm - 9-21-2007

    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 ( 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 =).

  4. luisabreu
    8:41 pm - 9-21-2007

    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
    rollback tran

    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!

  5. Mike
    4:38 am - 9-24-2007

    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.

  6. luisabreu
    8:27 am - 9-24-2007

    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.

  7. AR
    3:34 pm - 9-11-2008

    Did you try Casecade SaveUpdate on your collections