[英]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
. 我有两个实体, Dept
和Division
。 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.xhtml
和division.xhtml
来管理两个实体(CRUD操作)。 Both views are backed by DeptManager
and DivManager
, which are both ViewScoped. 这两个视图都由DeptManager
和DivManager
支持,它们都是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
并更改分配给特定Dept
的Division
, dept.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
分配给获取这些Entities
的View
的后备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.