我有一个我无法解决的奇怪问题。我有两个实体Dept
和Division
。 Dept
必须属于Division
。因此,每个depts
都有一个Division
列表。我还有两个视图dept.xhtml
和division.xhtml
来管理这两个实体(CRUD操作)。这两个视图均由DeptManager
和DivManager
支持,这两个视图都是ViewScoped。
division.xhtml
上有一个删除按钮,当所选的Division
附加了Dept
个实体时,该按钮将被禁用。当我转到dept.xhtml
并更改分配给特定Division
的{{1}}时,Dept
上的部门表格会正确更新。但是,当我导航到dept.xhtml
时,Division表不会捕获更新。显示最近修改的分区的Depts数的列中的值保留修改前的先前值。这会影响division.xhtml
上的DELETE按钮,当它不应该被禁用时。我必须关闭浏览器并在捕获更新之前重新打开它。我相信这不应该是因为我的导航导致每次创建一个新视图,我已经通过一些打印输出验证了。这是我的代码:
division.xhtml
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
的新实例获得相同的列表,除非会话无效。因此,返回缓存中的相同List of Divisions,而不是在updateList()
Bean find
上调用Session
方法。
有什么可能导致这种情况的想法?非常感谢。
答案 0 :(得分:0)
经过几天的故障排除后,我发现EntityManager
使用的Stateless Session Bean
保留了从数据库中检索到的Entities
缓存。因此,只要为Session Bean
的支持bean分配相同的View
,那么Entities
将始终返回缓存的副本,这可能不是最新的。< / p>
为了解决这个问题,我在我的支持bean中使用了布尔字段newInstance
。只要有人要求获取Entities
列表,就会检查此字段。如果newInstance
为真,我会遍历所有实体并从数据库中刷新它们。这样,只要访问的View
是新的(通过检查{{1}是否验证) } backing bean是新的),调用Viewscoped
将导致列表中的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);
}
}
以来已修改的实体并仅刷新那些实体,则会更好。我正在考虑实施这样的解决方案。
希望这会对某人有所帮助。