4

我很想知道在JSF-PrimeFaces / JPA堆栈中实现单页主从视图有哪些好的模式。似乎网络上的大多数材料和教程都只是在讨论相当琐碎的“每个视图一个表”模式。

但我对诸如在数据库中有CUSTOMERORDER表以及一个xhtml页面感兴趣,您可以在其中查看上半部分的客户(例如p:datatable)和下半部分当前选定客户的订单(再次作为p:datatable)。我不清楚如何最好地组织 JSF/PrimeFaces/backing bean 和外观/实体/JPA 代码,以一种普遍适用的方式实现上述目标,并最大限度地重用代码。例如

  1. 我应该为整个xhtml视图定义一个支持 bean,还是为视图的每个组件(主/详细信息)定义两个支持 bean?
  2. 可以将模式推广到多个明细表(在同一级别)吗?例如,在上半部分有一个CUSTOMER表的视图,在下半部分有一个选项卡式视图,分别由ORDERPAYMENT 明细表的两个视图组成(两者都与表CUSTOMER存在 N-1 关系
  3. 模式是否可以推广到多个详细级别(例如,在同一页面上有CUSTOMERINVOICEINVOICELINE表的视图。
  4. 提议的模式也能轻松适应修改。例如,使用可编辑的数据表可以更改客户详细信息并删除订单并使用提交更改按钮一次性完成这两项更改。
4

1 回答 1

4

对于最近的培训课程,我们构建了以下 proofOfConcept。我们创建了一个包含两个表的单个视图,一个主(部门)表和一个详细(员工)表。该视图由两个使用 cdi 事件进行通信的控制器 bean 支持。proofOfConcept 被部署到一个 jee6 容器(glassfish 3.1.1)。

这个想法是,每当您单击部门行时,ajax-Listener 都会触发一个 cdi-Event 来更新详细信息控制器并作为回报更新详细信息表。这种模式可以扩展到多个明细表或多个主-从-明细级别。

为了编辑您的实体,我建议打开一个编辑器对话框,例如在表格的每一行中添加一个编辑器按钮。要添加新的详细信息,请使用表格页脚中的 add-Actions 并使用新实体打开编辑器对话框。在您的编辑器对话框中的“确定”上,您再次使用新实体触发一个 cdi 事件以更新依赖的详细信息表。要保存您的工作,请使用单个“提交”按钮,它可以保存主实体。在训练中,我们将 jpa 与正确定义的实体一起使用,尤其是在 @OneToMany 关系中使用 orphanRemoval=true 属性。

视图(scott.xhtml):

<p:panel id="deptPanel" header="Departements">
    <p:dataTable id="deptTable" var="dept" value="#{deptUiController.departements}"
                 selectionMode="single" rowKey="#{dept.id}">
        <p:ajax event="rowSelect" listener="#{deptUiController.onRowSelect}" update="@form"/>
        <p:column headerText="Name">
            <h:outputText id="name" value="#{dept.dname}"/>
        </p:column>
        <p:column headerText="Location">
            <h:outputText id="loc" value="#{dept.loc}"/>
        </p:column>
        <p:column headerText="# of Emps">
            <h:outputText id="size" value="#{dept.emps.size()}"/>
        </p:column>
    </p:dataTable>
</p:panel>
<p:panel id="empPanel" header="Employees in departement #{deptUiController.currentDept.dname}">
    <p:dataTable id="empTable" var="emp" value="#{empUiController.employees}">
        <p:column headerText="Name">
            <h:outputText id="name" value="#{emp.ename}"/>
        </p:column>
        <p:column headerText="Job">
            <h:outputText id="job" value="#{emp.job}"/>
        </p:column>
        <p:column headerText="Hiredate">
            <h:outputFormat id="hiredate" value="{0,date,dd.MM.yyyy}">
                <f:param value="#{emp.hiredate}"/>
            </h:outputFormat>
        </p:column>
    </p:dataTable>
</p:panel>

主控制器:

@Named
@SessionScoped
public class DeptUiController implements Serializable {

    private boolean initialized = false;

    @EJB
    private ScottCRUD crudEJB;

    private List<Departement> departements;

    private Departement currentDept;

    public void populateData() {
        if ( !initialized ) {
            departements = crudEJB.findAllDepartements();
            currentDept =  departements.isEmpty() ? null : departements.get(0);
            initialized = true;
            fireDeptChange();
        }
    }

    @Inject
    private Event<Departement> deptChangeEvt;

    private void fireDeptChange() {
        deptChangeEvt.fire( currentDept );
    }

    public void onRowSelect(SelectEvent event) {
        currentDept = (Departement) event.getObject();
        fireDeptChange();
    }

    ... getter, setter, more actions...

}

细节控制器

@Named
@SessionScoped
public class EmpUiController implements Serializable {

    private List<Employee> employees;

    private Employee currentEmp;

    private void populateData(Departement master) {
        if ( master==null ) {
            employees = Collections.emptyList();
        } else {
            employees = master.getEmps();
        }
        currentEmp =  employees.isEmpty() ? null : employees.get(0);
    }

    public void observeDeptChanged( @Observes Departement master ) {
        populateData( master );
    }

    ... getter, setter, more actions...

}
于 2015-10-09T09:56:02.187 回答