9 Replies Latest reply on Aug 27, 2007 11:42 AM by pmuir

    Can action called from button avoid committing transaction?

    tynor

      Seam 1.2.1-GA, JBoss AS 4.2.1, Hibernate 3.2.5ga

      I wish to put a button on a form that adds an item to a list being displayed/edited in a dataTable. The new item is not completely initialized -- until the user types in some values in the table columns, it's not ready for persisting.

      On my MyHome class, I have a function

      public void addItem() {
       getInstance().myList().add(new SomeEntity());
      }
      


      The problem is that after calling addItem, something is deciding to commit the new SomeEntity to the database. I don't want it to try to persist until I explicitly call em.persist() or em.update(). How can I stop it from persisting the object?

      I've tried several types of button markup:

      <s:button action="#{myHome.addItem}" ...
      <h:command action="#{myHome.addItem}" ...
      <h:commandButton immediate="true"action="#{myHome.addItem}" ...
      


      ... and no matter what I do, hibernate is deciding to commit a transaction when I call through the button. Help!

      The stack in case that helps:

      org.hibernate.PropertyValueException: not-null property references a null or transient value: my.package.SomeEntity.name
       at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)
       at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:290)
       at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
       at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
       at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
       at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
       at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
       at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:644)
       at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:636)
       at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:323)
       at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
       at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
       at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
       at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
       at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
       at org.hibernate.event.def.DefaultPersistEventListener.entityIsPersistent(DefaultPersistEventListener.java:111)
       at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:84)
       at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:644)
       at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:636)
       at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:323)
       at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
       at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
       at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
       at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
       at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
       at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
       at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
       at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
       at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
       at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
       at org.hibernate.ejb.AbstractEntityManagerImpl$1.beforeCompletion(AbstractEntityManagerImpl.java:516)
       at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:114)
       at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:247)
       at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
       at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:177)
       at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1382)
       at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:135)
       at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:87)
       at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:140)
       at org.jboss.seam.jsf.AbstractSeamPhaseListener.commitOrRollback(AbstractSeamPhaseListener.java:324)
       at org.jboss.seam.jsf.TransactionalSeamPhaseListener.handleTransactionsAfterPageActions(TransactionalSeamPhaseListener.java:52)
       at org.jboss.seam.jsf.AbstractSeamPhaseListener.enterPage(AbstractSeamPhaseListener.java:285)
       at org.jboss.seam.jsf.AbstractSeamPhaseListener.beforeRender(AbstractSeamPhaseListener.java:214)
       at org.jboss.seam.jsf.SeamPhaseListener.beforePhase(SeamPhaseListener.java:56)
       at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:222)
       at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
       at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:63)
       at org.jboss.seam.debug.hot.HotDeployFilter.doFilter(HotDeployFilter.java:60)
       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
       at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
       at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:57)
       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
       at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:79)
       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
       at org.jboss.seam.web.SeamFilter.doFilter(SeamFilter.java:84)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:127)
       at org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:277)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
       at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
       at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
       at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:433)
       at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
       at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
       at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
       at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
       at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
       at java.lang.Thread.run(Thread.java:595)
      
      


        • 1. Re: Can action called from button avoid committing transacti
          matt.drees
          • 2. Re: Can action called from button avoid committing transacti
            tynor

            Thanks Matt.

            You probably want flushMode=manual


            Some problems with this:

            1) I've been trying to avoid dependencies on non-standard vendor extensions. (my read of the Seam manual is that this is Hibernate specific)

            2) I really just want to keep this specific function from triggering a commit. I'm happy to let my persist() function participate in a seam managed transaction - i just want to disable it for this action function.

            3) This requires that I explicitly start a new conversation. I'm currently using a seam-gen derived page.xml that says:
            <begin-conversation join="true"/>
            

            If I were to remove that from page.xml, where would I put the @Begin? On wire() ? Then I'll need to @End in any of my cancel/persist/update/remove actions (i.e. I'll need to not just use the ones inherited from EntityHome?). I don't want the manual flush to be propagated to other pages -- i really want to restrict it just to this action.


            There seems to be no way to tell the TransactionalSeamPhaseListener that this request ought not start a new transaction. Am I screwed?

            I should ask: Is there some other way for me to accomplish what I'm trying to do? (I need to dynamically grow a list by clicking a button and redisplay the page, where a dataTable contains the new item)


            • 3. Re: Can action called from button avoid committing transacti
              matt.drees

              For those more knowledgeable than me, please correct me if I'm wrong.

              1) Yes, it's hibernate-specific. But, at some point (jpa 2, hopefully), I'm guessing it won't be.

              2) I don't think you can live without a transaction. A transaction is needed even for reading from the db.

              3) Actually, you can do

               <begin-conversation join="true" flush-mode="manual"/>
              

              No, it isn't documented. Speaking of that, could you create a jira issue for that? That got me for a while, too.


              To do this without an atomic persistence context, I think you'd need to make sure the list containing the new SomeEntity is not in the persistence context when it is flushed at commit time. It's hard to suggest how to do that without knowing more of your code.

              Or maybe you could try changing cascadeType=All to cascadeType={Merge, Update, Delete} on the annotation for that relationship. I'm less confident about this solution.

              • 4. Re: Can action called from button avoid committing transacti
                matt.drees

                 

                "matt.drees" wrote:
                {Merge, Update, Delete}


                Oops. I meant {Merge, Refresh, Remove}.

                • 5. Re: Can action called from button avoid committing transacti
                  tynor

                  Thanks Matt - you got me on the right track.

                  I've got it working by removing the begin-conversation entity in my page.xml and annotating my wire() function as:

                  @Begin(flushMode=FlushModeType.MANUAL, join=true)
                  public void wire() {
                  


                  This wasn't as scary as I expected it to be. The persist()/update()/remove() functions from EntityHome all explicitly flush() the transaction, so they are inherently compatible with manual flush mode. (FWIW: manual page-level commits seem more natural than the alternative now that I've run into a simple case where the alternative is so broken - I sure hope this gets standardized by the time we need to port our app to non-Hibernate JPA implemenations...

                  FWIW, I wasn't able to use your suggestion of adding flush-mode in the page.xml:

                  <begin-conversation join="true" flush-mode="manual"/>


                  This doesn't work for me with Seam 1.2.1-GA -- perhaps it's something added for 2.0? In any case, since I can't confirm that it works, I don't feel justified in raising a JIRA - perhaps someone who uses it under 2.0 can raise the documentation issue?



                  • 6. Re: Can action called from button avoid committing transacti
                    matt.drees

                    Odd. It looks like 1.2.1 has code to parse the flush-mode attribute in pages.xml, so I think it ought to work. Oh well.

                    Glad things are working for you.

                    • 7. Re: Can action called from button avoid committing transacti
                      amitev

                      May i set flush mode from s:link/s:button?

                      • 8. Re: Can action called from button avoid committing transacti
                        matt.drees

                        I don't think so. I could be wrong.

                        • 9. Re: Can action called from button avoid committing transacti
                          pmuir

                          No you can't. File a feature request in JIRA if you want it.