PrimeFaces对话框编辑后AJAX更新DataTable的方案合理性咨询
我使用PrimeFaces DataTable实现了每行带编辑按钮的表格,点击编辑按钮可打开动态对话框。为避免对话框渲染时出现LazyLoad异常,我通过actionListener重新查询数据库并完整填充selectedItem。但在对话框中更新记录后,DataTable无法通过AJAX自动刷新。经排查,问题在于更新的对象并非表格列表中引用的原对象,而是actionListener中实例化的全新对象。我目前的解决方法是通过indexOf找到列表中对应位置,将该位置的对象替换为更新后的selectedItem。请问此方案是否为正确做法?是否会因Object的equals方法实现不当而引发问题?
相关代码
页面代码
<h:form id="formId"> <p:dataTable id="dataTableId" value="#{dataManagerView.listOfItems}" var="item"> <p:column headerText="操作"> <p:commandButton oncomplete="PF('editDialogWv').show()" update=":outlookDialogId" actionListener="#{dataManagerView.loadDataViewDetails(item)}" > <ui:remove>不能直接这么做,会触发LazyLoad异常 <f:setPropertyActionListener value="#{item}" target="#{dataManagerView.selectedItem}" /> </ui:remove> </p:commandButton> </p:column> </p:dataTable> </h:form>
<p:dialog id="outlookDialogId" dynamic="true" widgetVar="outlookDialogWv" > <h:form id="outlookFormId"> 若干表单输入框 <p:commandButton update="formId:dataTableId" action="#{dataManagerView.update()}"> </p:commandButton> </h:form> </p:dialog>
后端代码
public void loadDataViewDetails(Item item) { // 由actionListener调用 selectedItem = dataService.retrieveFullById(item.getId()); }
public void update() { dataService.update(selectedItem); // 只有手动刷新浏览器,表格中的选中项才会更新 }
修改后的更新方法:
public void update() { dataService.update(selectedItem); listOfItems.set(listOfItems.indexOf(selectedItem), selectedItem); }
你的方案是合理且可行的,但确实存在因equals方法实现不当引发问题的风险,具体分析如下:
方案的合理性
你通过retrieveFullById获取的是全新的对象实例,和DataTable列表中原有的对象并非同一引用,因此即使数据库数据已更新,列表里的旧对象引用不会自动同步。使用indexOf找到对应位置并替换,本质是让列表持有更新后的对象引用,这样AJAX刷新DataTable时就能读取到最新数据,这个逻辑是通顺的。equals方法的潜在风险
List.indexOf(Object o)的底层逻辑是遍历列表,逐个调用equals方法比较对象是否相等。如果你的Item类未正确实现equals(比如使用默认的Object.equals仅比较引用,或未基于业务主键实现),那么indexOf(selectedItem)会返回-1,导致listOfItems.set()抛出IndexOutOfBoundsException。
正确的Item类equals实现应基于业务主键(比如id字段),示例如下:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Item item = (Item) o; return Objects.equals(id, item.id); } @Override public int hashCode() { return Objects.hash(id); }
注意:实现equals时必须同时实现hashCode,这是Java规范要求,避免在使用散列集合(如HashSet)时出现异常。
- 可选优化方案
- 若不想依赖
equals方法,可在打开对话框时传递原对象的索引到后台:给DataTable添加varStatus="itemStatus",然后在commandButton中加入<f:attribute name="index" value="#{itemStatus.index}" />,在loadDataViewDetails方法中通过FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("index")获取索引并保存,更新时直接用该索引替换列表中的对象,彻底规避equals的问题。 - 也可在更新后重新查询整个列表(
listOfItems = dataService.listAll()),但这种方式在数据量大时性能不如单个对象替换。
内容的提问来源于stack exchange,提问作者jeff

