简体   繁体   English

修改后jsf视图不更新后备bean列表属性

[英]jsf view not updating backing bean list property after modification

I have a strange problem that I could not resolve. 我有一个无法解决的奇怪问题。 I have two entities, Dept and Division . 我有两个实体, DeptDivision A Dept must belong to a Division . Dept必须属于一个Division Thus, there is a list of depts for each Division . 因此,存在的一个列表depts每个Division I also have two views, dept.xhtml and division.xhtml to manage the two entities (CRUD Operations). 我还有两个视图dept.xhtmldivision.xhtml来管理两个实体(CRUD操作)。 Both views are backed by DeptManager and DivManager , which are both ViewScoped. 这两个视图都由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. division.xhtml上有一个DELETE按钮,当选定的Division附加了一些Dept实体时,将禁用该按钮。 When I go to dept.xhtml and change the Division assigned to a particular Dept , the Dept table on dept.xhtml is updated correctly. 当我转到dept.xhtml并更改分配给特定DeptDivisiondept.xhtml上的Dept表已正确更新。 However, when I navigate to division.xhtml , the Division table does not capture the update. 但是,当我导航至division.xhtml ,Division表无法捕获更新。 The value in the column showing the number of Depts for the recently-modified Division retains previous values before the modification. 显示最近修改的部门的部门数的列中的值保留修改之前的先前值。 This affects the DELETE button on division.xhtml , which might be disabled when it shouldnt be. 这会影响division.xhtml上的DELETE按钮,该按钮在不应该被禁用时可能被禁用。 I have to close the browser and re-open it before the update is captured. 在捕获更新之前,我必须关闭浏览器并重新打开它。 I believe this should not be because my navigation causes a new view to be created each time and I have verified that by some print-outs. 我认为这不应该是因为我的导航会导致每次创建一个新视图,并且我已经通过一些打印输出验证了这一点。 Here are my codes: 这是我的代码:

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;
    }

}

From my observation, it looks as if the field divisions is cached and new instances of viewScoped bean DivManager get the same list when updateList() is called, unless the session is invalidated. 根据我的观察,除非调用使会话无效,否则DivManager在调用updateList()时缓存了字段divisions并且viewScoped bean DivManager新实例获得了相同的列表。 Thus the same List of Divisions in the cache is returned instead of calling the find method on the Session Bean service . 因此,将返回高速缓存中相同的分区列表,而不是在Session Bean service上调用find方法。

Any idea on what could be causing this? 有什么可能的原因的想法吗? Thanks a lot. 非常感谢。

After several days of troubleshooting, I discovered that the EntityManager used by the Stateless Session Bean keeps a cache of Entities retrieved from the database. 经过几天的故障排除,我发现Stateless Session Bean使用的EntityManager保留了从数据库中检索到的Entities的缓存。 Therefore, as long as the same Session Bean is assigned to the backing bean of a View fetching those Entities will always return the cached copy, which might not be up-to-date. 因此,只要将相同的Session Bean分配给获取这些EntitiesView的后备Bean,它们将始终返回缓存的副本,该副本可能不是最新的。

To solve the problem, I made use of boolean field newInstance in my backing bean. 为了解决该问题,我在后备bean中使用了布尔字段newInstance This field is checked whenever there is a call to fetch the list of Entities . 每当调用获取Entities列表时,都会选中此字段。 If newInstance is true, I loop through all the entities and refresh them from the database.This way, as long as the View being visited is new (which is verified by checking if the Viewscoped backing bean is new), calling updateList will cause the Entities in the list to be refreshed. 如果newInstance为true,我将遍历所有实体并从数据库中刷新它们,这样,只要所访问的View是新的(通过检查Viewscoped后备bean是否为新来验证),则调用updateList将导致列表中的Entities需要刷新。 Here is the modified code: 这是修改后的代码:

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

And in the SSB service, I have the method: SSB服务中,我有以下方法:

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

Although this solved the problem, I envisage performance issue if the number of records is large. 尽管这解决了问题,但是如果记录数很大,我会想到性能问题。 If there is a way to monitor only the entities that have been modified since the last call to refreshList() and refresh only those, it will be better. 如果有一种方法可以仅监视自上次调用refreshList()以来已修改的实体,并仅刷新那些实体,那会更好。 I am thinking about implementing such a solution. 我正在考虑实施这样的解决方案。

Hope this will help someone. 希望这会帮助某人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM