14 Replies Latest reply on Jul 14, 2007 1:19 PM by ellenzhao

    flushMode= MANUAL, when does commit happen? And other questi

    ellenzhao

      Hi,

      I've read the latest reference section 8.3.3

      http://docs.jboss.com/seam/2.0.0.B1/reference/en/html/persistence.html#d0e5563

      and annotated my long-running conversation with @begin(flushMode=MANUAL), wrote em.flush(); in the @End method. But I still have some questions. When does the transaction commit happen?

      There are several nested conversation in the root conversation. A mother entity will be created at the beginning of the root conversation, persisted (in order to fetch the id) during the conversation. The nested conversations just add elements to the collections of the mother entity. I watched my shell output, during any nested conversation, each time when i say

      motherEntity.getChildren.add(sonEntity);
      

      hibernate inserted a sonEntity to the database and commits (or not commited? ) How to make the persistence of the son entities happen at the end of the root conversation?

      The hibernate.connection.autocommit is true by default, what's the consequence of setting it to false? I read the hibernate core reference, but did not get the answer.

      And, I want the transaction of the root conversation to roll back (the persisted motherEntity should be removed) when the root conversation is time out. How to achieve this?

      Any help would be highly appreciated!!


      Regards,
      Ellen

        • 1. Re: flushMode= MANUAL, when does commit happen? And other qu
          gavin.king

          Transaction commit is different to flushing the PC. Tx commits happen on every request. Flush occurs when you explicitly call entityManager.flush() or session.flush().

          Don't mess with hibernate.connection.autocommit, that is something completely unrelated.

          • 2. Re: flushMode= MANUAL, when does commit happen? And other qu
            ellenzhao

            Okay, thanks!

            How can the persisted motherEntity be removed when the root conversation is time out?

            • 3. Re: flushMode= MANUAL, when does commit happen? And other qu
              delphi'sghost

              AFAIK, you cannot perform multiple data entry conversations in a nested fashion without having an all-or-nothing problem with regards to saving the data.

              The problem is that each nested conversation uses the same persistence context, and when you flush for a nested conversation, you are flushing for all conversations using that persistence context (which includes all parent and child conversations).

              The conclusion I reached was that unfortunately, the nested conversation model was only good for browsing/navigating data due to the fact that it shares the persistence context. The Seam Issues example which was the only example to have editable pages in nested conversations was removed from the latest release (hope they re-write it to show how it should be done).

              Here are a couple of threads related to persistence contexts and nested conversations:


              http://www.jboss.com/index.html?module=bb&op=viewtopic&t=111681
              http://www.jboss.com/index.html?module=bb&op=viewtopic&t=111384

              However, thinking about your problem more, you may be able to do something like :

              Start root conversation (with flushmode = automatic)
              Create mother entity
              Persist mother entity
              Set the entityManager flush mode to Manual

              At this point, you are in a manual flush mode, with the mother entity already created and persisted. If the child item also has child items, then you are in a sticky problem. Also this solution doesn't handle the problem of :

              Edit Mother Entity
              Add child
              Edit child
              Flush entity manager to save child -> Ooops, we just accidentally saved the changes to the mother also since the child is edited in a nested conversation, and we flushed the shared persistence context.

              Also, if your 'add child' or 'edit child' links could be opened in new windows, then you could have accidental flushes in each conversation when you just meant to save and flush one instance of the child editor.

              As for rolling back the mother entity in the event of a conversation timeout...

              If you have a flag called isNew which indicates whether it is a new entity or not, you can write a cancel changes procedure, something like:

              String cancelChanges() {
               if (isNew) {
               em.remove(motherItem);
               } else {
               em.refresh(motherItem);
               }
              }
              
              


              I don't know the feasability of this, perhaps someone with more knowledge than me can comment, but you have a method marked with the @Destroy annotation, you could just call the cancel changes method from there if it has not already been saved (i.e. isNew is still true).

              This might be icky, but come to think of it, I'm wondering if this is a pretty handy pattern because what the conversation times out and you have made edits to the entity involved? Those edits could sit around in the persistence context after the conversation has timed out. Should the entity be refreshed because the persistence context might be re-used with the dirty object in it?



              • 4. Re: flushMode= MANUAL, when does commit happen? And other qu
                ellenzhao

                Hi DG!

                Thank you very much for your informative input! In my original post, I was dealing with the creation of a new motherEntity (which is a complex process...). So the isNew flag would always be true....However the flag idea is good. I can set a completedOrCanceled flag. Initialize it as false. The @End methods can be like this:

                
                @End
                public void cancelMotherCreation(){
                 // at this point, the children collections may or may not have been filled,
                 // but it does not matter, since we can cascade....
                 entityManager.remove(motherEntity);
                 this.completedOrCanceled = true;
                }
                
                @End
                public void newMotherDone(){
                 entityManager.merge(motherEntity);
                 this.completedOrCanceled = true;
                }
                


                Here the @Destroy method:

                @Destroy@Remove
                public void destroy(){
                 if (this.completedOrCanceled == false)
                 entityManager.remove(motherEntity);
                }
                


                I've got to try it out right now....


                Regards,
                Ellen

                • 5. Re: flushMode= MANUAL, when does commit happen? And other qu
                  ellenzhao

                  Oh by the way, I don't think this is icky at all...yes, a pretty handy pattern. My new-motherEntity.xhtml page has 5 or 6 parts are rounded with

                  <s:fragment rendered = .....>
                  ...
                  </s:fragment>
                  


                  And several of the fragment is modal (which is a nested conversation). The mother Entity itself it too simple to be displayed on a separate page and I want my users to keep a big picture in mind when she is creating this motherEntity(which has 5 or 6 collections). So, everything happens on the same page when the user is creating a new motherEntity.

                  It takes a bit work to get straight of issues and I have had long debugging sessions, but now the result turns out pretty good. Seems the rolling-back-at-time-out problem is the last issue I need to solve for this root conversation.


                  Regards,
                  Ellen

                  • 6. Re: flushMode= MANUAL, when does commit happen? And other qu
                    delphi'sghost

                    Origingally, my icky comment was related to the fact that I think you *might* run into a problem using the rollback code on a method annotated with @Remove and @Destroy since I don't know whether these annotations result in the method being called at other times (i.e. the bean is passivated, and not destroyed).

                    However, having a isCompleteOrCancelled flag would guard against any other calls you might get to that function.

                    • 7. Re: flushMode= MANUAL, when does commit happen? And other qu
                      ellenzhao

                      yes tested, it works....The @Destroy method is now like this....

                      @Remove
                       @Destroy
                       public void destroy() {
                       if (!this.completedOrCanceled) {
                       if (this.motherEntity.getId() != null) {
                       entityManager.merge(this.recipe); // without merging there might be exception saying you are trying to remove a detached object.....
                       entityManager.remove(this.recipe);
                       entityManager.flush();
                       }
                       }
                       }
                      


                      • 8. Re: flushMode= MANUAL, when does commit happen? And other qu
                        christian.bauer

                        This is very suspicious code, it should not work and this should be correct:

                        entityManager.remove( entityManager.merge(this.recipe) );
                        


                        Merge does not reattach the given instance, it returns an "attached" managed instance. Which is the right state for removing.

                        • 9. Re: flushMode= MANUAL, when does commit happen? And other qu
                          ellenzhao

                          Ah yes! Thanks Christian! There are more places I used JPA's merge as Hibernate's reattach, need to clean those up....

                          I thought about whether the entityManager.flush() at the end of the destroy() method is necessary. If this root conversation will be nested into yet another Überconversation later, then the flush makes sense. Otherwise it may well be unnecessary.

                          Regards,
                          Ellen

                          • 10. Re: flushMode= MANUAL, when does commit happen? And other qu
                            ellenzhao

                            I just cleaned up all the unnecessary/incorrect merges in my application, now the memory footprint is noticeably smaller. Thank Christian again for correcting me.

                            Unfortunately my original problem is not solved yet. I cannot seem to do flush in the @Destroy@Remove method, thus the removal of the incomplete motherEntity cannot be reflected in the database, the data integrity is harmed if any user leaves the conversation without explicitly ending it (either complete or cancel), or the server has a reboot when a conversation is in process....this is a pretty critical problem.

                            I tried explicit deletion using EQL in the @Destroy@Remove method too, it did not help.

                            The concrete error messages I got were always something like "no transaction available", still seeking a solution....Any suggestion will be highly appreciated!

                            • 11. Re: flushMode= MANUAL, when does commit happen? And other qu
                              ellenzhao

                              okay, added one more column to the table which represents the motherEntity. That column stores the status of the motherEntity. When it's created by the session bean, status is set to "creating". When user explicitly completed the creation, status is set to "created". Other status like "updating", "updated" can also be added and set correctly...

                              • 12. Re: flushMode= MANUAL, when does commit happen? And other qu
                                matt.drees

                                 

                                "ellenzhao" wrote:

                                The concrete error messages I got were always something like "no transaction available"


                                Well, you could always manually get a transaction with Transaction.instance() and begin/commit.

                                But probably the right way is marking the method as needing a transaction. If it's a javabean component, you could use @Transactional. I don't know much how Ejb works, but I imagine there's a way you can tell the container it needs a transaction, too.

                                • 13. Re: flushMode= MANUAL, when does commit happen? And other qu
                                  ellenzhao

                                  Thanks for Matt's tip, I tried this:

                                  @Destroy
                                   @Remove
                                   public void destroy() {
                                   UserTransaction tx = Transaction.instance();
                                   try {
                                   tx.begin();
                                   entityManager.remove(entityManager.merge(this.recipe));
                                   try {
                                   tx.commit();
                                   } catch (SecurityException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   } catch (IllegalStateException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   } catch (RollbackException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   } catch (HeuristicMixedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   } catch (HeuristicRollbackException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   }
                                   } catch (NotSupportedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   } catch (SystemException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                                   }
                                   }
                                  


                                  And got a javax.transaction.NotSupportedException when trying to shut down the jboss during a mother conversation.

                                  I wish Seam could provide something like @Begin(atomic = true). With this "atomic = true" flag on, if this conversation does not reach its own @end before time out or a application server crash, any insert/delete/update in the database could be rolled back, so that a long running conversation can map to an atomic database transaction and the correct semantic of an entity in the database can be guaranteed....Or if there is already something like this or there is some way I can achieve the atomic conversation behaviour, please let me know and I highly appreciate it!

                                  For now I add a "status" flag in the database, my application is aware that only records with the status "created" or "updated" are semantically correct and usable.

                                  • 14. Re: flushMode= MANUAL, when does commit happen? And other qu
                                    ellenzhao

                                    Feature request filed:

                                    http://jira.jboss.com/jira/browse/JBSEAM-1664

                                    I guess it is not uncommon for developers to mentally map a long-running conversation to an atomic DB transaction. If you feel this feature is also handy to you, please vote it on JIRA, thanks!