【发布时间】:2012-10-07 21:36:16
【问题描述】:
我在一个项目中,我会写很多这样的页面,所以我想使用最有效的(编写)编码模式。
背景:过去我使用 CODI 的 @ViewAccessScoped 来保存请求之间的状态,最近我开始使用 flash 作用域对象来保存状态。我不能使用 JSF @ViewScoped,因为我使用 CDI,而且它们不能很好地配合使用。所以我想看看我是否可以只使用 @RequestScoped 支持 bean 来做到这一点。
页面是这样设计的(p命名空间是Primefaces):
<f:metadata>
<f:viewParam name="ID" value="#{backing.id}" />
</f:metadata>
....
<h1>Edit Object Page</h1>
<h:form id="formObj" rendered="#{backing.accessOK}">
<p:panelGrid columns="2">
<h:outputLabel value="Field #1:"/>
<p:inputText value="#{backing.record.field1}" />
(more input fields)
<h:outputLabel value="Action:" />
<h:panelGroup>
<p:commandButton value="Save"
action="#{backing.save}"
/>
<p:commandButton value="Cancel" action="backing.cancel" />
</h:panelGroup>
</p:panelGrid>
<p:messages showDetail="true" showSummary="true" />
</h:form>
如果页面被请求,accessOK() 方法可以阻止 h:form 被呈现。相反,p:messages 与 accessOK() 方法需要设置的任何 FacesMessage(s) 一起显示。
bean backing 的模式如下所示:
@Named
@RequestScoped
public class Backing {
private long id;
private SomeJPAEntity record;
private Boolean accessOK;
public long getId() { return id; }
public void setId(long value) { id = value; }
public boolean accessOK() {
if (accessOK != null) return accessOK;
if (getRecord() == null) {
// add a FacesMessage that explains the record
// does not exist
return accessOK = false; // note single =
}
// do any other access checks, such as write permissions
return accessOK = true;
}
public SomeJPAEntity getRecord() {
if (record != null) return record;
if (getId() > 0) record = // get the record from DB
else record = new SomeJPAEntity();
return record;
}
public String execute() {
if (!accessOK()) return null; // bad edit
// do other integrity checks here. If fail, set FacesMessages
// and return null;
if (getId() > 0) // merge the record back into the data base
else // persist the record
}
}
这是该模型的问题所在。当点击 Save 按钮时,会构建一个新的 Backing 实例,然后会大量调用 getRecord() getter before setID() 设置器被调用。所以 getRecord() 中的逻辑中断,因为它不能依赖 id 属性在调用时是有效的。
如果这是一个 @ViewAccessScoped(或 ViewScoped)支持 bean,那么 id 和 record使用 commandButton 处理表单时已经设置了属性。或者,您可以将这些属性保存在闪存中,但这有其自身的问题,我想避免。
那么有没有办法让这个编程模型在规范中工作?
更新:
我有一个变通方法,凭经验允许它在不诉诸闪存或视图范围变体的情况下工作。如上所述,处理表单时,在第一次调用 setId() 之前 会调用多次getRecord()。然后最后调用 save() 方法。
我所做的是修改 setId() 如下:
public void setId(long value) {
id = value;
record = null;
}
这会强制 getRecord() 重新计算(即获取/构建)属性 record,然后后续调用工作。具体来说,输入 save() 方法时,对象已正确初始化,包括 record 实体的内容。我不知道之前对 getRecord() 的所有调用是为了什么。
在这一点上,我也不知道我可以依赖的是是否所有写入 record 实体的值都会从 UIinput 组件中传输出来在 getId() 设置之后。如果是这样,那么我可以使用这种模式。这是由规范控制还是只是靠运气?
有什么建议或建议吗?
【问题讨论】:
-
为什么要停止使用 CODI 的范围?你遇到过他们的问题吗?
-
@MikeBraun 我担心它可移植到 Glassfish 以外的容器中。它还有许多不同的模块,这些模块应该独立工作但没有,这让我担心它的部署稳定性。但真正的原因是我想用 RequestScoped 来完成这项工作,只是为了在您可能有成千上万的用户打开编辑屏幕时减少服务器上的负载。尽管如此,如果我不能让这个 Code Pattern 稳健地工作,那我会回去。
-
CODI 和 DeltaSpike 是您可以为 Glassfish 获得的最佳便携式 CDI 扩展。我在许多生产项目中使用 CODI 以及不同的 EE6 服务器。
-
我明白了。您可能有兴趣听说在 JSF 2.2 中视图范围将支持 CDI bean。如果成千上万的用户打开编辑屏幕并经常进行回发,那么内存中的大量数据确实可以为您节省数千次访问数据库的次数。