【问题标题】:jsf view not updating backing bean list property after modificationjsf视图在修改后不更新支持bean列表属性
【发布时间】:2014-04-11 19:29:07
【问题描述】:

我有一个无法解决的奇怪问题。我有两个实体,DeptDivisionDept 必须属于 Division。因此,每个Division 都有一个depts 列表。我还有两个视图,dept.xhtmldivision.xhtml 来管理这两个实体(CRUD 操作)。这两个视图都由 DeptManagerDivManager 支持,它们都是 ViewScoped。

There is a DELETE button on division.xhtml, which is to be disabled when the selected Division has some Dept entities attached to it.当我转到dept.xhtml 并更改分配给特定DeptDivision 时,dept.xhtml 上的 Dept 表会正确更新。但是,当我导航到 division.xhtml 时,Division 表没有捕获更新。显示最近修改部门的部门数的列中的值保留了修改前的先前值。这会影响division.xhtml 上的 DELETE 按钮,该按钮可能在不应该被禁用时被禁用。在捕获更新之前,我必须关闭浏览器并重新打开它。我相信这不应该是因为我的导航每次都会创建一个新视图,并且我已经通过一些打印输出验证了这一点。这是我的代码:

dept.xhtml

<h:body>
        <ui:composition template="adminTemplate.xhtml">
            <ui:define name="content">
                <h:form id="form">
                    <p:dataTable id="dtable" value="#{deptManager.orderedDepts}" var="dept" 
                                 selectionMode="single" selection="#{deptManager.dept}" rowIndexVar="index"  
                                 rowKey="#{dept.deptCode}" scrollable="true" scrollHeight="270">
                        <p:ajax event="rowSelect" listener="#{deptManager.onRowSelect}" update=":form:fields"  />

                        <f:facet name="header">List of Departments</f:facet>
                        <p:column headerText="S/No">#{index+1}</p:column>
                        <p:column headerText="Department Code" sortBy="deptCode">#{dept.deptCode}</p:column>
                        <p:column headerText="Department Name" sortBy="description" >#{dept.description}</p:column>
                        <p:column headerText="Division" sortBy="description" >#{dept.divCode.description}</p:column>
                        <f:facet name="footer">Number of Departments #{deptManager.count}</f:facet>
                    </p:dataTable>
                    <br/><br/>
                    <p:panel  header="Create/Modify Dept" toggleable="true" toggleOrientation="vertical">
                        <p:panelGrid columns="2" id="fields">
                            <p:outputLabel value="Department Code:"/> <p:inputText value="#{deptManager.dept.deptCode}" disabled="true" />
                            <p:outputLabel value="Department Description:"/> <p:inputText value="#{deptManager.dept.description}" />                  
                            <p:outputLabel value="Department Division"/> 
                            <p:selectOneMenu value="#{deptManager.dept.divCode}" converter="omnifaces.SelectItemsConverter">
                                <f:selectItem itemLabel="Select.." noSelectionOption="true" />
                                <f:selectItems value="#{divManager.orderedDivs}" itemLabel="#{division.description}" var="division" itemValue="#{division}" />
                            </p:selectOneMenu>  
                        </p:panelGrid>
                        <p:commandButton ajax="true" actionListener="#{deptManager.createNew}" value="NEW" update="@form" />
                        <p:commandButton ajax="true" actionListener="#{deptManager.deleteDept}" value="DELETE" update="@form" />
                        <p:commandButton ajax="true" actionListener="#{deptManager.saveDept}" value="SAVE" update="@form"  />
                    </p:panel>  

                </h:form>                                
            </ui:define>            
        </ui:composition>
    </h:body>

DeptManager

@Named
@ViewScoped
public class DeptManager implements Serializable{  
    @EJB
    private DeptFacade service;   

    private Dept dept;
    private List<Dept> depts;

    @Inject
    private DivManager divManager;

    public DeptManager() {

    }

    public DivManager getDivManager() {
        return divManager;
    }

    public void setDivManager(DivManager divManager) {
        this.divManager = divManager;
    }

    @PostConstruct
    public void Init(){
        dept = new Dept();
        updateList();
    }

    public void setService(DeptFacade service) {
        this.service = service;
    }

    public Dept getDept() {
        if(dept == null){
            dept = new Dept();
        }
        return dept;
    }

    public void updateList(){
        depts = service.findOrderedAll("Dept", "deptCode");
    }    

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public int getCount(){
        return service.count();
    }

    public List<Dept> getDepts(){
        return service.findAll();
    }

    public List<Dept> getOrderedDepts(){
        return depts;
    }

    public String createNew(){
        dept = new Dept();
        dept.setDeptCode("");
        dept.setDivCode(null);
        dept.setDescription("");
        return "";
    }

    public String saveDept(){
        //code to create or edit Dept
        updateList();
        divManager.updateList();
        return "dept.xhtml?faces-redirect=true";
    }

    public String deleteDept(){
        if (dept.getDeptCode() == null || dept.getDeptCode().isEmpty()){
            return "dept";
        }
        service.remove(dept);
        dept = new Dept();
        updateList();
        divManager.updateList();
        return "dept";
    }

    public String onRowSelect(SelectEvent event) {  
        dept = (Dept) event.getObject();
        return "";
    } 

    public String navigate(String page){
        return page;
    }

}

division.xhtml

<h:body>
        <ui:composition template="adminTemplate.xhtml">
            <ui:define name="content">
                <h:form id="form">
                    <p:dataTable id="dtable" value="#{divManager.orderedDivs}" var="division" 
                                 selectionMode="single" selection="#{divManager.division}" rowIndexVar="index"  
                                 rowKey="#{division.divCode}" scrollable="true" scrollHeight="300">
                        <p:ajax event="rowSelect" listener="#{divManager.onRowSelect}" update=":form:fields"  />

                        <f:facet name="header">List of Divisions</f:facet>
                        <p:column headerText="S/No">#{index+1}</p:column>
                        <p:column headerText="Division Code" sortBy="divCode">#{division.divCode}</p:column>
                        <p:column headerText="Division Name" sortBy="description" >#{division.description}</p:column>
                        <p:column headerText="No Of Depts"  >#{division.invDeptList.size()}</p:column>
                        <f:facet name="footer">Number of Divisions: #{divManager.count}</f:facet>
                    </p:dataTable>
                    <br/><br/>
                    <p:panel id="fields" header="Create/Modify Division" toggleable="true" toggleOrientation="vertical">
                        <p:panelGrid columns="2" >
                            <p:outputLabel value="Division Code:"/> <p:inputText value="#{divManager.division.divCode}" disabled="true" />
                            <p:outputLabel value="Division Description:"/> <p:inputText value="#{divManager.division.description}" />                  
                        </p:panelGrid>
                        <p:commandButton ajax="true" actionListener="#{divManager.createNew}" value="NEW" update="@form" />
                        <p:commandButton ajax="true" actionListener="#{divManager.deleteDiv}" disabled="#{divManager.division.deptList.size() > 0}" value="DELETE" update="@form" />
                        <p:commandButton ajax="true" actionListener="#{divManager.saveDiv}" value="SAVE" update="@form"  />
                    </p:panel>  

                </h:form>                                
            </ui:define>            
        </ui:composition>
    </h:body>

DivisionManager

@Named
@ViewScoped
public class DivManager implements Serializable{

    @EJB
    private DivFacade service;

    private Div division;   
    private ArrayList<Div> divisions;  
    private boolean newInstance;

    public DivManager() {

    }

    @PostConstruct
    public void Init(){
        newInstance = true;
        division = new Div();
        divisions = new ArrayList<Div>();
        updateList();
        System.out.println("DivManager bean "+this.toString()+" was created");
    }

    public Div getDivision() {
        if(division == null){
            division = new Div();
        }
        return division;
    }

    public void setService(DivFacade service) {
        this.service = service;
    }

    public void updateList(){
        divisions.clear();
        divisions.addAll(service.findOrderedAll("Div", "divCode"));
        // Also tried: divisions = service.findOrderedAll("Div", "divCode"); same result
    }    

    public void setDivision(Div div) {
        this.division = div;
    }

    public int getCount(){
        return service.count();
    }

    public List<Div> getDivisions(){
        return service.findAll();
    }

    public List<Div> getOrderedDivs(){
        if(newInstance){
            updateList();
            newInstance = false;
        }
        return divisions;
    }

    public String createNew(){
        division = new Div();
        division.setDivCode("");        
        division.setDescription("");
        return null;
    }

    public String saveDiv(){
        //code to modify or create Division
        updateList();
        return "division.xhtml?faces-redirect=true";
    }

    public String deleteDiv(){
        if (division.getDivCode() == null || division.getDivCode().isEmpty()){
            return null;
        }
        service.remove(division);
        division = new Div();
        updateList();        
        return null;
    }

    public String onRowSelect(SelectEvent event) {  
        division = (Div) event.getObject();
        return null;
    } 

    public String navigate(String page){
        return page;
    }

}

根据我的观察,看起来好像字段 divisions 已缓存,并且 viewScoped bean DivManager 的新实例在调用 updateList() 时获得相同的列表,除非会话无效。因此,在缓存中返回相同的分区列表,而不是在 Session Bean service 上调用 find 方法。

您知道是什么原因造成的吗?非常感谢。

【问题讨论】:

    标签: jsf jsf-2 primefaces


    【解决方案1】:

    经过几天的故障排除,我发现Stateless Session Bean使用的EntityManager保留了从数据库中检索到的Entities的缓存。因此,只要将相同的 Session Bean 分配给 View 的支持 bean,获取那些 Entities 将始终返回缓存的副本,这可能不是最新的。

    为了解决这个问题,我在支持 bean 中使用了布尔字段 newInstance。每当调用获取Entities 的列表时,都会检查此字段。如果newInstance 为真,我循环遍历所有实体并从数据库中刷新它们。这样,只要访问的View 是新的(通过检查Viewscoped 支持bean 是否是新的来验证) ,调用updateList 将导致列表中的Entities 被刷新。这是修改后的代码:

    @DivManager
    public void updateList(){
        service.refreshList(divisions);
    }   
    

    而在SSB服务中,我有方法:

    public void refreshList(List<Div> divisions){
        EntityManager em = getEntityManager();
        for(Div divison : divisions){
            division = em.find(Div.class, division.getDivCode());
            em.refresh(division);
        }
    }
    

    虽然这解决了问题,但如果记录数量很大,我会想到性能问题。如果有一种方法可以仅监视自上次调用 refreshList() 以来已修改的实体并仅刷新这些实体,那就更好了。我正在考虑实施这样的解决方案。

    希望这会对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 2011-06-26
      • 2013-01-17
      • 2012-10-18
      • 2013-05-07
      • 2011-06-29
      • 2012-06-25
      • 1970-01-01
      • 2012-01-30
      • 1970-01-01
      相关资源
      最近更新 更多