1 2 Previous Next 17 Replies Latest reply on Dec 18, 2007 9:21 AM by asavitsky

    EntityQuery.refresh() doesn't clear the result list??

      Not sure whether it's a bug, or just me, but it doesn't look like the refresh() method is doing anything for me - the result list is stale and never refreshes, until I start a new coversation...

      I have a simple CRUD screen where the edit form is combined with the record listing:

      <h:form id="listForm" styleClass="form">
       <ui:repeat var="transaction" value="#{userTransactions.resultList}">
       <h:commandLink action="#{transactionController.select(transaction)}" value="#{transaction.account.accountNumber} " />
       </ui:repeat>
       <h:commandButton action="#{transactionController.create}" value="Add New" />
      </h:form>
      <h:form id="rowForm" styleClass="form" rendered="#{not empty selectedTransaction}">
      <!-- form fields go here -->
       <h:commandButton action="#{transactionController.save}" value="Save" />
       <h:commandButton action="#{transactionController.remove}" value="Remove" immediate="true" rendered="#{not selectedTransaction.new}" />
       <h:commandButton action="#{transactionController.cancel}" value="Close" immediate="true" />
      </h:form>

      The query for record listing is an EntityQuery defined in components.xml:
      <framework:entity-query name="userTransactions" scope="conversation" order="transactionDate, securityName">
       <framework:ejbql>
       FROM Transaction
       WHERE user = #{principal}
       </framework:ejbql>
      </framework:entity-query>

      and the controller code has the EntityQuery injected, and refreshes the list as necessary:
      @Name ("transactionController")
      @Scope (ScopeType.CONVERSATION)
      public class TransactionController extends BaseController {
       @Out (required = false)
       private Transaction selectedTransaction;
       @Inprivate EntityQuery userTransactions;
      
       public void cancel() {
       selectedTransaction = null;
       }
       public void create() {
       selectedTransaction = new Transaction();
       }
       @Transactional
       public void remove() {
       em.remove(selectedTransaction);
       em.flush();
       selectedTransaction = null;userTransactions.refresh();
       }
       @Transactional
       public void save() {
       em.persist(selectedTransaction);
       em.flush();
       selectedTransaction = null;userTransactions.refresh();
       }
       public void select(Transaction transaction) {
       selectedTransaction = transaction;
       }
      }

      The whole thing runs in a long conversation, started when the page is entered:
      <page view-id="/attestation/transactions.xhtml">
       <begin-conversation flush-mode="manual" join="true" />
      </page>


      Now, any time a new record is inserted, or an old one removed, the list should refresh, shouldn't it? It doesn't, and I'm running out of any ideas on to why would this happen... Help, anyone?

      Thanks,

      Alex

        • 1. Re: EntityQuery.refresh() doesn't clear the result list??
          damianharvey

          Why do you want your Query scoped to the conversation? I would have thought that this was the cause of your problem.

          • 2. Re: EntityQuery.refresh() doesn't clear the result list??

             

            "damianharvey" wrote:
            Why do you want your Query scoped to the conversation?


            So that all entities queried by this query would still be managed by the same entity manager that is used in the conversation. If I declare the query in session scope, I run into "Entity is not managed by this session" exceptions.

            Now, why would the conversation scope be the cause of the problem, if it's running a long conversation? The conversation stays active during the whole described interaction, I verified that in the logs.

            • 3. Re: EntityQuery.refresh() doesn't clear the result list??
              damianharvey

              I meant, why not put it in the Page or Event scope.

              • 4. Re: EntityQuery.refresh() doesn't clear the result list??

                Page- or event-scoped query would be recreated (and reexecuted) for each and every action performed on the page, including record navigation/selection. My idea was to minimize database hits by caching the queried list in conversation scope, only updating it when I know its state would change, i.e. when a record gets updated/inserted/deleted.

                • 5. Re: EntityQuery.refresh() doesn't clear the result list??

                  I must add - sometimes the queried list would get quite big (say, if it were the audit record that were queried, it might be measured in millions of records), so it would be lazy-paged in this case, and re-executing such queries on every page action wouldn't be a good idea...

                  • 6. Re: EntityQuery.refresh() doesn't clear the result list??
                    gavin.king

                     

                    Page- or event-scoped query would be recreated (and reexecuted) for each and every action performed on the page


                    This is definitely NOT true of the PAGE context! (That's the whole point of why we have a page context.)

                    Now, any time a new record is inserted, or an old one removed, the list should refresh, shouldn't it? It doesn't, and I'm running out of any ideas on to why would this happen... Help, anyone?


                    How about using your debugger to find out why? Sheesh!

                    Now, personally, I use after transaction success events for this kind of thing, like in the booking example.

                    • 7. Re: EntityQuery.refresh() doesn't clear the result list??

                      First, thanks for the pointers. I'll look up the "after transaction" event in the CVS. It must have been a freshly added one, as it's not listed in the Seam 2B docs... yet.

                      Now, in regards to

                      How about using your debugger to find out why ? Sheesh!
                      where did I say that I didn't use one? Just tried to spare everyone the gory details, as my concern was mainly whether I use the components in a correct way - but since you asked, here goes...

                      To trace the problem, I used a modified EntityQuery class, with some logging added to the methods that manipulate resultList. Only modified methods are shown for brewity.
                      package org.jboss.seam.framework;
                      public class EntityQuery extends Query<EntityManager> {
                       private static final Log log = LogFactory.getLog(EntityQuery.class);
                       private List resultList;
                      
                       private void initResultList() {
                       log.warn("Entered initResultList(), ejbql " + getEjbql()
                       + ", hashcode " + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       if (resultList == null) {
                       javax.persistence.Query query = createQuery();
                       resultList = query == null ? null : query.getResultList();
                       }
                       log.warn("Exited initResultList(), ejbql " + getEjbql() + ", hashcode "
                       + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       }
                       @Transactional
                       @Override
                       public List getResultList() {
                       log.warn("Entered getResultList(), ejbql " + getEjbql() + ", hashcode "
                       + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       if (isAnyParameterDirty()) {
                       refresh();
                       }
                       initResultList();
                       try {
                       return truncResultList(resultList);
                       } finally {
                       log.warn("Exited getResultList(), ejbql " + getEjbql()
                       + ", hashcode " + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       }
                       }
                       @Override
                       @Transactional
                       public boolean isNextExists() {
                       log.warn("Entered isNextExists(), ejbql " + getEjbql() + ", hashcode "
                       + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       return resultList != null && resultList.size() > getMaxResults();
                       }
                       @Override
                       public void refresh() {
                       log.warn("Entered refresh(), ejbql " + getEjbql() + ", hashcode "
                       + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       super.refresh();
                       resultCount = null;
                       resultList = null;
                       singleResult = null;
                       log.warn("Exited refresh(), ejbql " + getEjbql() + ", hashcode "
                       + this.hashCode() + ", list size "
                       + (resultList == null ? "null" : resultList.size()));
                       }
                      }


                      And here's the log output during the execution of controller's save() method for a new record:

                      WARN [org.jboss.seam.framework.EntityQuery] - Entered refresh(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5
                      WARN [org.jboss.seam.framework.EntityQuery] - Exited refresh(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size null
                      WARN [org.jboss.seam.framework.EntityQuery] - Entered getResultList(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5
                      WARN [org.jboss.seam.framework.EntityQuery] - Entered initResultList(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5


                      ejbql and hashcode output is mainly to make sure we're dealing with the right query, and that it stays the same object during execution (i.e., not dropped and re-created).

                      As you can see, refresh() resets the resultList to null all right, yet the next time query gets accessed, it still contains the same old result list (refreshed one would contain 6 items, as one was added)! There are no modifications to resultList in between these calls, as evident from both the logs, and from the breakpoints I've been placing. My suspicions were at the javassist, that with the bytecode enhancement on EntityQuery not all modifications to resultList would be caught by debugger, but since I don't know jack about javassist and bytecode, I didn't investigate those.

                      Now, I hope I explained that it's not because I'm lazy to use a debugger, that I'm running out of ideas on why is this problem happening?

                      • 8. Re: EntityQuery.refresh() doesn't clear the result list??
                        gavin.king

                         

                        I'll look up the "after transaction" event in the CVS. It must have been a freshly added one, as it's not listed in the Seam 2B docs... yet.


                        It's not new at all, it's been used in the booking demo for a while. Events.instance().raiseAfterTransactionSuccessEvent(...)

                        As you can see, refresh() resets the resultList to null all right, yet the next time query gets accessed, it still contains the same old result list


                        Probably because the transaction is not yet committed and so the new data is not yet flushed by Hibernate to the database.

                        Either:

                        * flush the session manually OR
                        * use an after transaction success event to call refresh


                        • 9. Re: EntityQuery.refresh() doesn't clear the result list??

                          Thanks for your help Gavin.

                          There's a manual flush right before the refresh() call:

                          public void save() {
                          em.persist(selectedAccount);
                          em.flush();
                          selectedAccount = null;
                          userAccounts.refresh();
                          }


                          and the data does indeed get into the database. Here's the same log, this time with SQL logging included:

                          WARN [org.jboss.seam.framework.EntityQuery] - before em.persist()
                          Hibernate: select SEQ_ACCOUNT.nextval from dual
                          WARN [org.jboss.seam.framework.EntityQuery] - before em.flush()
                          Hibernate: insert into Account (CREATED_BY, CREATED_ON, MODIFIED_BY, MODIFIED_ON, ACCOUNT_NUMBER, closed, DEALER_NAME, discretionary, FAMILY_EXEMPTION, NAME_ON_STATEMENT, USER_ID, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                          Hibernate: update USERS set MODIFIED_BY=?, MODIFIED_ON=?, BLACKOUT_WINDOW=?, BUSINESS_UNIT=?, COVERAGE_REASON=?, DEPARTURE_DATE=?, email=?, FULL_NAME=?, MANAGER_EMAIL=?, MANAGER_NAME=?, role=?, START_DATE=?, status=?, TEMPLATE_ID=?, title=?, username=? where id=? and MODIFIED_ON=?
                          WARN [org.jboss.seam.framework.EntityQuery] - before refresh()
                          WARN [org.jboss.seam.framework.EntityQuery] - Entered refresh(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5
                          WARN [org.jboss.seam.framework.EntityQuery] - Exited refresh(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size null
                          WARN [org.jboss.seam.framework.EntityQuery] - after refresh()
                          Where's the SQL select that refreshes list?
                          WARN [org.jboss.seam.framework.EntityQuery] - Entered getResultList(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5
                          WARN [org.jboss.seam.framework.EntityQuery] - Entered initResultList(), ejbql FROM Account WHERE closed = false AND familyExemption = false AND user = #{principal}, hashcode 10610605, list size 5
                          


                          But that's not even what baffles me. If resultList was set to null, it would have to be refreshed from the query. Now, where's the SELECT that refreshes the list? As you see, no SQL SELECT is logged between the places where resultList is null and where it's 5 items long again!

                          I'm suspecting that one of the interceptors resets the query state to what it was before the save() call - but again, it must be doing it in a way that my debugger doesn't catch. At least, the "variable modification" breakpoint in Eclipse doesn't catch that update.

                          I'll try it with the after transaction event, to see if it makes any difference, but still, it does look strange...

                          • 10. Re: EntityQuery.refresh() doesn't clear the result list??

                            Nope, raiseAfterTransactionSuccessEvent() didn't help. Here's the code I used:

                            @Observer ("refreshAccounts")
                             public void refresh() {
                             userAccounts.refresh();
                             }
                             @Transactional
                             public void save() {
                             em.persist(selectedAccount);
                             em.flush();
                             selectedAccount = null;
                             Events.instance().raiseTransactionSuccessEvent("refreshAccounts");
                             }


                            and the result is exactly the same, both behavior-wise and log-wise.

                            Now, the EntityQuery is declared as "@In private EntityQuery userAccounts;" - does it also need to be outjected if I change its internals, for the change to take effect?

                            • 11. Re: EntityQuery.refresh() doesn't clear the result list??
                              gavin.king

                               

                              I'm suspecting that one of the interceptors resets the query state to what it was before the save() call


                              Put a breakpoint in ManagedEntityIdentityInterceptor and see if its doing something "funny".

                              • 12. Re: EntityQuery.refresh() doesn't clear the result list??

                                Indeed, it does :(

                                Tracing code in ManagedEntityIdentityInterceptor:

                                private static final Log log = LogFactory.getLog(ManagedEntityIdentityInterceptor.class);
                                 private void getFromWrapper(Object bean, Field field, Object dataModel)
                                 throws Exception {
                                 Object value = Contexts.getConversationContext().get(getFieldId(field));
                                 if (value != null) {
                                 if (dataModel == null) {
                                 if (field.getName().equals("resultList"))
                                 log.warn("getFromWrapper: "
                                 + bean.getClass().getSimpleName() + "."
                                 + field.getName() + " = " + value);
                                 Reflections.set(field, bean, value);
                                 } else {
                                 setWrappedData(dataModel, value);
                                 }
                                 }
                                 }
                                 private void saveWrapper(Object bean, Field field, Object dataModel,
                                 Object value) throws Exception {
                                 if (field.getName().equals("resultList"))
                                 log.warn("saveWrapper: conversation." + getFieldId(field) + " = "
                                 + value);
                                 Contexts.getConversationContext().set(getFieldId(field), value);
                                 if (dataModel == null) {
                                 if (field.getName().equals("resultList"))
                                 log.warn("saveWrapper: " + bean.getClass().getSimpleName()
                                 + "." + field.getName() + " = null");
                                 Reflections.set(field, bean, null);
                                 } else {
                                 setWrappedData(dataModel, null);
                                 }
                                 }
                                


                                Tracing in EntityQuery:
                                private void initResultList() {
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Entered initResultList: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 if (resultList == null) {
                                 javax.persistence.Query query = createQuery();
                                 resultList = query == null ? null : query.getResultList();
                                 }
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Exited initResultList: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 }
                                 @Transactional
                                 @Override
                                 public List getResultList() {
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Entered getResultList: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 if (isAnyParameterDirty()) {
                                 refresh();
                                 }
                                 initResultList();
                                 try {
                                 return truncResultList(resultList);
                                 } finally {
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Exited getResultList: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 }
                                 }
                                 @Override
                                 public void refresh() {
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Entered refresh: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 super.refresh();
                                 resultCount = null;
                                 resultList = null;
                                 singleResult = null;
                                 if (getEjbql() != null && getEjbql().startsWith("FROM Account")) {
                                 log.warn("Exited refresh: hashcode " + this.hashCode()
                                 + ", list = " + resultList);
                                 }
                                 }
                                


                                Tracing in AccountController (my code):
                                @Transactional
                                 public void save() {
                                 log.warn("save: before persist");
                                 em.persist(selectedAccount);
                                 log.warn("save: before flush");
                                 em.flush();
                                 selectedAccount = null;
                                 log.warn("save: before refresh");
                                 userAccounts.refresh();
                                 log.warn("save: after refresh");
                                 }
                                


                                Log output on new record insert (resultList is supposed to go from one item to two):
                                WARN [com.tdam.ptss.controller.AccountController] - save: before persist
                                Hibernate: select SEQ_ACCOUNT.nextval from dual
                                WARN [com.tdam.ptss.controller.AccountController] - save: before flush
                                Hibernate: insert into Account (CREATED_BY, CREATED_ON, MODIFIED_BY, MODIFIED_ON, ACCOUNT_NUMBER, closed, DEALER_NAME, discretionary, FAMILY_EXEMPTION, NAME_ON_STATEMENT, USER_ID, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                                WARN [com.tdam.ptss.controller.AccountController] - save: before refresh
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - getFromWrapper: EntityQuery.resultList = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Entered refresh: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Exited refresh: hashcode 27469166, list = null
                                WARN [com.tdam.ptss.controller.AccountController] - save: after refresh
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - getFromWrapper: EntityQuery.resultList = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Entered getResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Entered initResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Exited initResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Exited getResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - saveWrapper: conversation.userAccounts.resultList = [Account[87]]
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - saveWrapper: EntityQuery.resultList = null
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - getFromWrapper: EntityQuery.resultList = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Entered getResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Entered initResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Exited initResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.framework.EntityQuery] - Exited getResultList: hashcode 27469166, list = [Account[87]]
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - saveWrapper: conversation.userAccounts.resultList = [Account[87]]
                                WARN [org.jboss.seam.persistence.ManagedEntityIdentityInterceptor] - saveWrapper: EntityQuery.resultList = null
                                


                                Indeed, it does gets reset to old value right after I try to set it to null, and, as it uses reflection, it's no wonder why debugger wasn't catching it :(.

                                Now, is that considered to be the normal behavior (in which case - are there any ways to disable this interception?), or is this a bug (in which case I'll submit it to JIRA)?

                                Thanks,

                                Alex

                                • 13. Re: EntityQuery.refresh() doesn't clear the result list??
                                  gavin.king

                                  If you have a simple, runnable test case that reproduces the problem against current Seam2 CVS then sure, submit it to JIRA.

                                  • 14. Re: EntityQuery.refresh() doesn't clear the result list??

                                    Looks like it was already fixed in CVS, so I'll be waiting on the next release then...

                                    1 2 Previous Next