java 为什么从数据库加载支持 bean 中的新数据后 jsf 视图没有更新?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/733674/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:33:49  来源:igfitidea点击:

Why the jsf view is not updated after new data in the backing bean are loaded from database?

javajsfjakarta-ee

提问by alexmeia

I am - unfortunally - working with jsf and I am experiencing a problem. I have a jsf page that displays a table with some data, using the <h:dataTable>component. Every row of this table has an <h:commandLink>with a Remove action to remove the item in that row. When I perform that action, the method in the backing bean is correctly called, the ArrayListthat contains the table items is correctly uptaded, and the navigation method to come back in the same page where the table is, is correctly performed.

我 - 不幸的是 - 使用 jsf 并且我遇到了问题。我有一个 jsf 页面,它使用<h:dataTable>组件显示一个包含一些数据的表格。此表的每一行都有一个<h:commandLink>带有删除操作的操作以删除该行中的项目。当我执行该操作时,支持 bean 中的方法被正确调用,包含表项的ArrayList被正确更新,并且返回到表所在页面的导航方法被正确执行。

But when the page is reloaded the table is not updated. I see the same items that were in the table before the remove action.

但是当页面重新加载时,表不会更新。我看到删除操作之前表中的相同项目。

If I reload again the page, now the table is updated.

如果我再次重新加载页面,现在表格已更新。

It seems just like the view is rendered before that the backing bean has completed its updates.

看起来就像在支持 bean 完成更新之前呈现视图一样。

How can I force to render the view just when the backing bean has completed its updates?

如何在支持 bean 完成更新后强制呈现视图?

Here some code:

这里有一些代码:

The commandLink in the jsf page, inside the dataTable:

jsf页面中的commandLink,dataTable里面:

<h:column>
<f:facet name="header">
    <h:outputText value="Rimuovi" />
</f:facet>
<h:commandLink action="#{servicesListBean.removeServizio}" title="Rimuovi questo servizio" onclick="if(!confirm('Vuoi davvero rimuovere questo servizio?')) return false">
    <h:outputText value="Rimuovi" />
</h:commandLink>
</h:column>

And here the method in the backing bean:

这里是支持 bean 中的方法:

public String removeServizio() {
        this.servizioToRemove = (Servizio) getDataTable().getRowData();
        try {
        ServiziDAO.removeServizio(this.servizioToRemove.getId());
        } catch (Exception e) {
            // ...
        }

        return userSessionBean.goToElencoServizi(); 
    }

In this case, I gave request scopeto the backing bean. I already tried to give it session scope and call a method to update the list, but the result is the same.

在这种情况下,我为支持 bean提供了请求范围。我已经尝试给它会话范围并调用一个方法来更新列表,但结果是一样的。

I hope I was clear enough. Thanks to everyone in advance.

我希望我说得够清楚了。提前感谢大家。



UPDATE TO THIS QUESTION THAT CHANGES EVERYTHING

更新这个改变一切的问题

Thanks to everyone. As McDowell said, the problem was not in the time when view was rendered, and the problem was not JSF related at all.

谢谢大家。正如 McDowell 所说,问题不在于渲染视图的时间,问题根本与 JSF 无关。

What happened is that when I removed an item from the DB, I did not delete the row, I just set an end date to the item. This is the right way to do it in my application, but while in the query that setted the end date to the item I took the time using System.currentTimiMillis(), in the query that reloaded the uptaded list I used the sysdateof the Oracle server.

发生的情况是,当我从数据库中删除一个项目时,我没有删除该行,我只是为该项目设置了结束日期。这是在我的应用程序中执行此操作的正确方法,但是在将结束日期设置为我花时间使用的项目System.currentTimiMillis()的查询中,在重新加载更新列表的查询中,我使用sysdate了 Oracle 服务器的 。

There are some fractions of second of difference between this two dates, and that's why I could see the updated list just when I reloaded the page: because one ore two second were passed!

这两个日期之间有几分之一秒的差异,这就是为什么我可以在重新加载页面时看到更新的列表:因为一两秒过去了!

Maybe this issue can be useful to someone else.

也许这个问题对其他人有用。

回答by McDowell

It seems just like the view is rendered before that the backing bean has completed its updates.

看起来就像在支持 bean 完成更新之前呈现视图一样。

The only way this could happen is if you did the work in another thread - but it doesn't look like you are doing that.

发生这种情况的唯一方法是,如果您在另一个线程中完成了这项工作 - 但看起来您并没有这样做。

I am guessing, but it looks like a logic problem in your backing bean. Is your servicesListBeankeeping state? That is:

我在猜测,但它看起来像是您的支持 bean 中的逻辑问题。您的servicesListBean是否保持状态?那是:

  1. HTTP POST
  2. The RESTORE VIEWphase of the lifecycle causes servicesListBeanto be created in the request scope (each phase of the lifecycle needs to iterate over every row)
  3. servicesListBeanfetches the rows from the persistence store and they are cached in the bean state
  4. When we get to the INVOKE APPLICATIONphase, removeServizio()removes the target row from the persistence store, but does not do anything about the cached bean state
  5. The RENDER RESPONSEphase renders the stale cached bean state
  6. If you refresh now, the bean gets recreated with new cached values and you see the correct state
  1. HTTP POST
  2. 生命周期的RESTORE VIEW阶段导致在请求范围内创建servicesListBean(生命周期的每个阶段都需要迭代每一行)
  3. servicesListBean从持久性存储中获取行并将它们缓存在 bean 状态中
  4. 当我们进入INVOKE APPLICATION阶段时,removeServizio()从持久性存储中删除目标行,但不对缓存的 bean 状态做任何事情
  5. 渲染响应阶段呈现过时缓存Bean状态
  6. 如果您现在刷新,bean 将使用新的缓存值重新创建,并且您会看到正确的状态

This backing bean recreates the problem:

这个支持 bean 重现了这个问题:

public class DeleteFromRowBean {

  private ListDataModel dataModel;

  public DataModel getList() {
    if (dataModel == null) {
      List<RowBean> list = PersistenceStore.fetch();
      dataModel = new ListDataModel(list);
    }
    return dataModel;
  }

  public String deleteCurrentRow() {
    if (dataModel == null) {
      throw new IllegalStateException();
    }
    RowBean row = (RowBean) dataModel.getRowData();
    PersistenceStore.delete(row.getId());
    return null; // no navigation required
  }

}

Modifying the list while the data table is iterating over it is not a good idea (you'll get concurrent modification exceptions). The easiest solution in my sample bean is just to release the reference to the cached data. The data table already has a reference to this for the duration of the iteration in the INVOKE APPLICATIONphase; when the RENDER RESPONSEstarts, the data table will call getList()again, causing the new state to be fetched from the persistence store.

在数据表迭代时修改列表不是一个好主意(你会得到并发修改异常)。我的示例 bean 中最简单的解决方案就是释放对缓存数据的引用。在INVOKE APPLICATION阶段的迭代期间,数据表已经有了对此的引用;当RENDER RESPONSE开始时,数据表将再次调用getList(),导致从持久性存储中获取新状态。

public class DeleteFromRowBean {

  private ListDataModel dataModel;

  public DataModel getList() {
    if (dataModel == null) {
      List<RowBean> list = PersistenceStore.fetch();
      dataModel = new ListDataModel(list);
    }
    return dataModel;
  }

  public String deleteCurrentRow() {
    if (dataModel == null) {
      throw new IllegalStateException();
    }
    RowBean row = (RowBean) dataModel.getRowData();
    PersistenceStore.delete(row.getId());
    // flush cached data
    dataModel = null;
    return null; // no navigation required
  }

}

回答by Slartibartfast

If you don't explicitly redirect to "another" page (even if it's the same page), you will get old data. It's feature. Try something like:

如果您没有明确重定向到“另一个”页面(即使它是同一页面),您将获得旧数据。是特点。尝试类似:

public String removeServizio() {
    .
    .
    .
    userSessionBean.goToElencoServizi(); // if this redirects to needed page
    return null;
}