13 Replies Latest reply on Dec 2, 2009 1:05 AM by nagendra_singh_krishnawat

    Refresh issue with conversation scoped page

    nbhatia.bhatian.comcast.net

      I have a very strange problem with a conversation scoped page. The page shows a list of orders. I also have an update button on the page which should query the database and show an updated list of orders. Unfortunately, no matter how many times I click the update button, I am getting the same set of (stale) orders that were displayed when the page was first loaded! Can somebody help me identify what I am doing wrong?


      Here's the update button on my page:


      <h:commandButton
          value="Update"
          action="#{orderListController.findOrders}" />
      



      Here's the bean whose findOrders() method is triggered (incidently it is also a factory method to take care of the initial display):


      @Name("orderListController")
      @Scope(ScopeType.CONVERSATION)
      public class OrderListController implements Serializable {
      
          @DataModel(scope=ScopeType.PAGE)
          private List<Order> orders;
      
          @Factory("orders")
          public void findOrders() {
              Session session = (Session)entityManager.getDelegate();
              Criteria orderCriteria = session.createCriteria(Order.class);
              ...
              orders = (List<Order>)orderCriteria.list();
          }
      }
      



      Here's the table on the same page that is refreshed as a result of the action:


      <h:dataTable id="orderTable" value="#{orders}" var="order">
          ...
      </h:dataTable>
      



      I start a conversation when the page is entered:


      <page ... >
          <begin-conversation join="true" />
      </page>
      



      The issue is this - when the update button is clicked, I can see that findOrders() is called, but the query returns a stale list of orders. I am guessing that the problem is related to the hibernate session. I have double checked that my flush mode is auto and not manual, so I should be hitting the database every time to get fresh data. Any clues about what I may be doing wrong?


      Thanks.


      Naresh

        • 1. Re: Refresh issue with conversation scoped page
          nbhatia.bhatian.comcast.net

          Any thoughts on this issue? Could it be that the entity manager is not fetching fresh instances of Order entities because they are already sitting in the session? In that case, is conversation scope a bad idea for this use case?


          Thanks.


          Naresh

          • 2. Re: Refresh issue with conversation scoped page
            asookazian

            The factory method will only be executed if orders is null.  If you are posting back to the same JSF page, it will not be null b/c you are using PAGE scope.


            Try using:


            @DataModel(scope=ScopeType.EVENT)



            Another option is to not use @Factory and call the method directly as follows:


            <h:dataTable id="orderTable" value="#{orderListController.findOrders}" var="order">



            but make sure to check for null in the findOrders() method to avoid unnecessary db hits.  Add a debug brkpt in the method to see when it's actually being called...

            • 3. Re: Refresh issue with conversation scoped page
              nbhatia.bhatian.comcast.net

              Arbi, I tried with

              @DataModel(scope=ScopeType.EVENT)

              but that gives me the following runtime error:


              java.lang.IllegalArgumentException:
              @DataModel scope must be ScopeType.UNSPECIFIED or ScopeType.PAGE: orderListController
              



              You also mentioned that



              Another option is to not use @Factory and call the method directly as follows:

              <h:dataTable id="orderTable" value="#{orderListController.findOrders}" var="order">





              That's pretty much I am doing in when the update button is clicked:


              <h:commandButton
                  value="Update"
                  action="#{orderListController.findOrders}" />
              



              It is calling findOrders() directly, which should update the orders context variable and the DataTable should display that. As noted above, all that is happening - the trouble is that findOrders() is not returning the right results. Although the underlying database has been changed by some other transaction, findOrders() is not picking up the change.


              If I change the scope of OrderListController itself to Event, then it is picking up the changes by another transaction (obviously - because it is not keeping a hibernate session open). But the down side is that selection of table rows now stops working, i.e DataModelSelection does not work. So it seems that I can get one thing or the other, but not both.


              Any further ideas?


              Thanks.


              Naresh

              • 4. Re: Refresh issue with conversation scoped page
                nbhatia.bhatian.comcast.net

                I just confirmed that the problem is because orders are being cached in Hibernate's session cache. In the hibernate query above, if I simply clear the session before querying, I do get fresh instances of orders from the database. Unfortunately that can't be my solution because clearing the session clears out many other objects that my front end relies on and thus other things break.


                Has anyone come across this problem? How did you solve it?


                Thanks.


                Naresh

                • 5. Re: Refresh issue with conversation scoped page
                  javacoryd

                  If you can't do a clear() on the entityManager you can loop over the list of orders calling entityManager.refresh( order ).


                  Cory.

                  • 6. Re: Refresh issue with conversation scoped page
                    nbhatia.bhatian.comcast.net

                    Thanks Cory. That works! The only drawback is that it is doing a select for every order. Hey, but at least it works!


                    I may play around with another approach where I don't use @DataModelSelection. This would allow me to get rid of the conversation and use plain old event scope.

                    • 7. Re: Refresh issue with conversation scoped page
                      javacoryd

                      An alternative to this approach which doesn't do a single query for each order is to drill down to the Hibernate Session ( em.getDelegate() ) and explicitly evict() each order.  You can then re-run your query and it will reload the orders from the DB.


                      Cory.

                      • 8. Re: Refresh issue with conversation scoped page
                        nbhatia.bhatian.comcast.net

                        Good suggestion! I tried it, but running into another problem with this strategy. The evict is happening fine, but the following findOrders() call is trying to do a flush, which is forcing the detached orders to persist via some cascade relationship and Hibernate does not like that:


                        Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: org.archfirst.bullsfirst.domain.order.Order
                             at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
                             at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:671)
                             at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:663)
                             at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:346)
                             at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
                             at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
                             at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
                             at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:319)
                             at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:265)
                             at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:242)
                             at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
                             at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
                             at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:154)
                             at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145)
                             at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
                             at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
                             at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:996)
                             at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1589)
                             at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:306)
                             at JpaAccountRepository.findOrders(JpaAccountRepository.java:102)
                        
                        

                        • 9. Re: Refresh issue with conversation scoped page
                          javacoryd

                          Yea, the reason this is happening is because you probably have a relationship from Order to something as a OneToMany, OneToOne etc, and the evict() is not cascading to those relationships.  EJB3 doesn't support the evict, but if you are using JBoss it works by adding an additional Cascade annotation from Hibernate to your relationship with a cascade type of EVICT.


                          Like:
                          @Cascade(org.hibernate.annotations.CascadeType.EVICT)


                          Cory.

                          • 10. Re: Refresh issue with conversation scoped page
                            nbhatia.bhatian.comcast.net

                            Thanks Cory. That's good to know.

                            • 11. Re: Refresh issue with conversation scoped page

                              ok, I see that you have declared both @DataModel and @Factory which are outjecting same context variable order, Since Data model itself is outjecting order in scope of conversation you dont need to say another @Factory.


                              You can also define the scope of @datamodel (See datamodel attributes)

                              • 12. Re: Refresh issue with conversation scoped page
                                nbhatia.bhatian.comcast.net

                                Good observation - hadn't thought about that. However, this is what I have seen in all Seam examples (see this example). I understand that the factory is used to initialize the variable when none exists, whereas @DataModel outjects to support clickable links.

                                • 13. Re: Refresh issue with conversation scoped page


                                  @Factory
                                  @Factory("processInstance")
                                  Specifies that the method of the component is used to initialize the value of the named context variable, when the context variable has no value. This style is used with methods that return void.
                                  
                                  @Factory("processInstance", scope=CONVERSATION)
                                  Specifies that the method returns a value that Seam should use to initialize the value of the named context variable, when the context variable has no value. This style is used with methods that return a value. If no scope is explicitly specified, the scope of the component with the @Factory method is used (unless the component is stateless, in which case the EVENT context is used).
                                  
                                  value — specifies the name of the context variable. If the method is a getter method, default to the JavaBeans property name.
                                  
                                  scope — specifies the scope that Seam should bind the returned value to. Only meaningful for factory methods which return a value.



                                  Read the value description.