我在这里遇到了一个似乎比我更好的问题。
语境
我有显示 JPA 实体详细信息的 ap:dialog (primefaces)。在此对话框中,用户可以修改信息并提交。提交后,信息将保存在 DB 中(通过 JPA)。
p:dialog 被“包装”在一个组件中,以便在不同的情况下使用。
问题:
如果我显示带有从 DB 中选择的实体的对话框,请修改一些信息并单击保存(提交)按钮:它第一次工作正常。数据库被更新并且 p:dialog 被隐藏。
如果我再次显示具有相同实体的对话框,请再次修改 p:dialog 中的数据并再次提交:一切似乎都工作正常(确认消息和日志)但数据库未更新。
在任何时候,在 p:dialog 中找到的信息都是正确的。但是,在 BackingBean 和 DB 中找到的数据只有在第一次提交后才是最新的。
另一个线索:如果在提交操作后我刷新页面,它将再次提交。
调试信息:
根据提交发布的信息(Firebug 长寿!!!)所有发布的数据都是正确的(始终是最新的)。但是如果我在日志中显示支持 bean 接收到的内容,它与之前提交的内容相同(而不是新信息)。
数据已正确发布,但似乎收到/解释不当。这就像问题发生在接收提交的帖子时,在 JSF 生命周期的 RestoreView、ApplyRequestValue、ProcessValidation 或 UpdateModelValue 阶段之一中。
因此,第二次保存(提交)似乎有效但无效的原因是因为保存在数据库中的数据对于每次后续提交都是相同的.....为什么?
我使用 Glassfish 3.1.2、Mojarra 2.1.13、JSF、PrimeFaces、CDI、JPA、Hibernate...
代码片段:
包含 p:dialog (cmpnt:dataEntryDialog) 作为组件的页面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:cmpnt="http://java.sun.com/jsf/composite/component"
>
<h:head>
<title>
Test!!!
</title>
</h:head>
<h:body>
<h:commandButton type="button" onclick="PVDlg.show();" value="show dlg"/>
<cmpnt:dataEntryDialog id="PVDataEntry" video="#{processStatus.testedVideo}" fieldGroupId="2"
header="This is a test"
widgetVar="PVDlg" render="@this"/>
</h:body>
</html>
p:dialog 的组件实现(cmpnt:dataEntryDialog):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<composite:interface componentType="dataEntryComponent">
<composite:attribute name="video" required="true"/>
<composite:attribute name="widgetVar" required="true"/>
<composite:attribute name="render" required="false"/>
<composite:attribute name="closeActionListener" method-signature="void listener(org.primefaces.event.CloseEvent)" required="false" />
[... other attributes]
</composite:interface>
<composite:implementation>
<h:outputStylesheet library="css" name="dataEntryDialog.css" target="head" />
<div id="#{cc.clientId}">
<script type="text/javascript">
function handleDataEntrySaveAttempt(xhr, status, args) {
if( args.validationFailed || !args.saved ) {
jQuery('#' + #{cc.clientId} + ':cmpntDataEntryDialog').effect("shake", {times:3}, 100);
} else {
${cc.attrs.widgetVar}.hide();
}
return true;
}
</script>
<p:dialog
id="cmpntDataEntryDialog"
header="#{cc.attrs.header}"
widgetVar="#{cc.attrs.widgetVar}"
resizable="false"
showEffect="fade"
hideEffect="fade"
dynamic="true"
minimizable="true"
maximizable="false"
width="550"
height="500"
>
<h:form id="cmpntDataEntryForm" style="position:relative;">
<div style="height:460px;">
<div style="width:100%;height:100%;margin-bottom:5px;overflow:auto;">
<ui:repeat value="#{dataEntryDialog.loadDataEntryFields( cc.attrs.video, cc.attrs.fieldGroupId )}" var="field" varStatus="fieldRankInfo">
<div style="position:relative;width:100%;height:24px;margin:4px 0px;">
<h:outputText value="#{field.fieldName}:" style="vertical-align:middle;" styleClass="margin"/>
<p:calendar id="cmpntInputDate" value="#{field.value}" rendered="#{'java.util.Date' eq field.type}" styleClass="input margin" effect="drop" />
<p:selectOneMenu id="cmpntInputComboBox" value="#{field.value}" rendered="#{'ComboBox' eq field.type}" styleClass="input margin">
<f:selectItem itemLabel="SelectAValue"/>
<f:selectItems value="#{field.possibleValues}"/>
<f:converter converterId="com.ctec.world.ConvertInteger" />
</p:selectOneMenu>
[... some other optionally rendered fields for different data types]
</div>
</ui:repeat>
</div>
</div>
<div style="position:relative;bottom:0;">
<div style="float:right;margin-top:5px;">
<p:commandButton id="cmpntSubmit" action="#{dataEntryDialog.save( cc.attrs.video.video )}"
value="${cc.resourceBundleMap.dataEntryDialog_Save}"
process="@form" update="${cc.attrs.render} @form"
styleClass="margin" oncomplete="handleDataEntrySaveAttempt( xhr, status, args )"/>
<p:commandButton id="cmpntCancel"
value="${cc.resourceBundleMap.dataEntryDialog_Cancel}"
onclick="${cc.attrs.widgetVar}.hide();" styleClass="margin"/>
</div>
</div>
</h:form>
</p:dialog>
</div>
</composite:implementation>
</html>
关于托管 bean,它们是普通的 CDI @Named @SessionScoped bean。
附加信息:
我做了进一步的测试:通过阶段监听器查看是否有任何可从那里访问的对象具有有趣的信息......直到现在还没有运气。