primefaces
我的日常工作涉及大量使用数据。 我们使用关系数据库来存储所有内容,因为我们依赖于企业级的数据管理。 有时,具有将数据提取为简单格式(例如电子表格)的功能很有用,以便我们可以按需进行操作。 这篇文章概述了我使用PrimeFaces 5.0生成有效且易于使用的基于JSF的数据导出实用程序所采取的步骤。 导出实用程序将生成一个电子表格,其中包括列标题。 用户可以选择要导出的数据库字段以及应该以什么顺序导出。
我们要确保我们拥有一个直观的干净用户界面。 因此,我选择不在屏幕上显示任何数据。 而是,用户界面包含PrimeFaces PickList组件,该组件列出了可供选择的不同数据字段,以及用于生成导出的按钮。 首先,设置数据库基础结构以使此导出实用程序成为可能。
对于本文,我增强了AcmePools应用程序,该应用程序是通过我在OTN上发布的名为PrimeFaces in the Enterprise的文章开发的。 导出实用程序允许将客户数据导出到电子表格中。 客户数据包含在NetBeans安装在Apache Derby内的示例数据库中,或者您可以在此帖子中使用SQL脚本。 在创建此导出实用程序之前,请在您的环境中下载或创建AcmePools项目。
数据导出实用程序分为两部分,第一部分是PrimeFaces PickList组件,供用户选择要导出的字段,第二部分是将所选字段内容提取到电子表格中的导出按钮。 最终结果将类似于图1所示的用户界面。
开发PickList组件
首先,创建数据基础结构以支持PickList组件。 它由一个数据库表组成,用于保存您要导出的实体数据的列名和标签,以及一个可选的数据库序列,用于填充该表的主键。 在这种情况下,数据库表名为COLUMN_MODEL,我们用与CUSTOMER数据库表的数据库列名称相对应的实体字段名称填充该表。
-- Add support for data export
create table column_model(
id int primary key,
column_name varchar(30),
column_label varchar(150));
-- Optional sequence for primary key generation
create sequence column_model_s
start with 1
increment by 1;
-- Load with field (database column) names
insert into column_model values(
1,
'addressline1',
'Address Line 1');
insert into column_model values(
2,
'addressline2',
'Address Line 2');
insert into column_model values(
3,
'city',
'City');
insert into column_model values(
4,
'creditLimit',
'Credit Limit');
insert into column_model values(
5,
'customerId',
'Customer Id');
insert into column_model values(
6,
'discountCode',
'Discount Code');
insert into column_model values(
7,
'email',
'Email');
insert into column_model values(
8,
'fax',
'Fax');
insert into column_model values(
9,
'name',
'Name');
insert into column_model values(
10,
'phone',
'Phone');
insert into column_model values(
11,
'state',
'State');
insert into column_model values(
12,
'zip',
'Zip');
接下来,创建一个实体类,该实体类可用于从组件内部访问列数据。 如果您使用NetBeans之类的IDE,则可以通过向导非常轻松地完成此操作。 如果使用NetBeans,请右键单击com.acme.acmepools.entity包,然后选择“新建”->“数据库中的实体类”,然后为我们的示例数据库选择数据源。 当表列表填充时,选择COLUMN_MODEL表,如图2所示。最后,选择“下一步”和“完成”以创建实体类。
完成后,名为ColumnModel的实体类应如下所示:
package com.acme.acmepools.entity;
import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author Juneau
*/
@Entity
@Table(name = "COLUMN_MODEL")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ColumnModel.findAll", query = "SELECT c FROM ColumnModel c"),
@NamedQuery(name = "ColumnModel.findById", query = "SELECT c FROM ColumnModel c WHERE c.id = :id"),
@NamedQuery(name = "ColumnModel.findByColumnName", query = "SELECT c FROM ColumnModel c WHERE c.columnName = :columnName"),
@NamedQuery(name = "ColumnModel.findByColumnLabel", query = "SELECT c FROM ColumnModel c WHERE c.columnLabel = :columnLabel")})
public class ColumnModel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "ID")
private BigDecimal id;
@Size(max = 30)
@Column(name = "COLUMN_NAME")
private String columnName;
@Size(max = 150)
@Column(name = "COLUMN_LABEL")
private String columnLabel;
public ColumnModel() {
}
public ColumnModel(BigDecimal id) {
this.id = id;
}
public BigDecimal getId() {
return id;
}
public void setId(BigDecimal id) {
this.id = id;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnLabel() {
return columnLabel;
}
public void setColumnLabel(String columnLabel) {
this.columnLabel = columnLabel;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof ColumnModel)) {
return false;
}
ColumnModel other = (ColumnModel) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.acme.acmepools.entity.ColumnModel[ id=" + id + " ]";
}
}
接下来,为新生成的实体类创建一个EJB会话bean,以便组件可以查询列数据。 您也可以根据需要使用IDE。 如果使用NetBeans,请右键单击com.acme.acmepools.session包,然后选择“新建”->“实体类的会话Bean”。 对话框打开后,从左侧列表中选择实体类“ com.acme.acmepools.entity.ColumnModel”,然后单击“完成”(图3)。
创建会话bean之后,添加一个名为findId()的方法,该方法可用于根据指定的列名返回列ID值。 ColumnModelFacade的完整资源应如下所示:
package com.acme.acmepools.session;
import com.acme.acmepools.entity.ColumnModel;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
*
* @author Juneau
*/
@Stateless
public class ColumnModelFacade extends AbstractFacade {
@PersistenceContext(unitName = "com.acme_AcmePools_war_AcmePools-1.0-SNAPSHOTPU")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public ColumnModelFacade() {
super(ColumnModel.class);
}
public ColumnModel findId(String columnName){
return (ColumnModel) em.createQuery("select object(o) from ColumnModel as o " +
"where o.columnName = :columnName")
.setParameter("columnName", columnName)
.getSingleResult();
}
}
接下来,创建一些帮助程序类,这些类将用于在PickList组件中加载和管理数据。 第一个类名为ColumnBean,它用于存储实体数据,然后将其传递给PickList以供使用。 ColumnBean的代码是一个简单的POJO:
package com.acme.acmepools.bean;
import java.math.BigDecimal;
/**
*
* @author juneau
*/
public class ColumnBean {
private BigDecimal id;
private String columnName;
private String columnLabel;
public ColumnBean(BigDecimal id, String columnName, String columnLabel){
this.id = id;
this.columnName = columnName;
this.columnLabel = columnLabel;
}
/**
* @return the id
*/
public BigDecimal getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(BigDecimal id) {
this.id = id;
}
/**
* @return the columnName
*/
public String getColumnName() {
return columnName;
}
/**
* @param columnName the columnName to set
*/
public void setColumnName(String columnName) {
this.columnName = columnName;
}
/**
* @return the columnLabel
*/
public String getColumnLabel() {
return columnLabel;
}
/**
* @param columnLabel the columnLabel to set
*/
public void setColumnLabel(String columnLabel) {
this.columnLabel = columnLabel;
}
}
PickList组件需要使用PrimeFaces DualListModel来访问和更新数据。 因此,我们必须实现一个类,该类可用于将实体数据强制到ColumnBean POJO中,然后将其存储到DualListModel中,以便PickList组件可以使用它。 在下面的名为PickListBean的类中,构造函数接受作为实体数据的List <ColumnModel>作为参数,执行强制转换,然后将其存储到DualListModel <ColumnBean>集合中,以供组件使用。
package com.acme.acmepools.bean;
/**
*
* @author juneau
*/
import java.util.ArrayList;
import java.util.List;
import com.acme.acmepools.entity.ColumnModel;
import org.primefaces.model.DualListModel;
public class PickListBean {
private DualListModel<ColumnBean> columns;
private List<ColumnBean> source = null;
private List<ColumnBean> target = null;
public PickListBean(List<ColumnModel> columnModelList) {
//Columns
source = new ArrayList<ColumnBean>();
target = new ArrayList<ColumnBean>();
for(ColumnModel column:columnModelList){
ColumnBean bean = new ColumnBean(column.getId(), column.getColumnName(), column.getColumnLabel());
source.add(bean);
}
columns = new DualListModel<ColumnBean>(source, target);
}
public DualListModel<ColumnBean> getColumns() {
return columns;
}
public void setColumns(DualListModel<ColumnBean> columns) {
this.columns = columns;
}
}
最后,我们需要创建一个控制器类来访问所有这些数据。 为此,请在com.acme.acmepools.jsf包中创建一个名为ColumnModelController的类,并通过使用@Named和@SessionScoped对其进行注释,使其成为CDI托管bean。 使该类实现Serializable。 初始控制器类应如下所示(我们稍后将对其进行更新以包括促进导出的方法):
@Named
@SessionScoped
public class ColumnModelController implements Serializable {
@EJB
ColumnModelFacade ejbFacade;
private PickListBean pickListBean;
private List<ColumnModel> columns;
public DualListModel<ColumnBean> getColumns() {
pickListBean = new PickListBean(ejbFacade.findAll());
return pickListBean.getColumns();
}
public void setColumns(DualListModel<ColumnBean> columns) {
pickListBean.setColumns(columns);
}
}
如您所见,getColumns()方法查询ColumnModel实体,该实体通过PickListBean构造函数填充DualListModel <ColumnBean>。
这将处理数据库基础结构和业务逻辑……现在让我们看一下用于PickList的PrimeFaces组件。 以下摘录摘自WebPages / poolCustomer / CustomerExport.xhtml视图,其中包含PickList组件的标记:
<p:panel header="Choose Columns for Export">
<p:picklist effect="bounce" itemlabel="#{column.columnLabel}" itemvalue="#{column.columnName}" showsourcecontrols="true" showtargetcontrols="true" value="#{columnModelController.columns}" var="column">
<f:facet name="sourceCaption">Columns</f:facet>
<f:facet name="targetCaption">Selected</f:facet>
</p:picklist>
</p:panel>
如您所见,PickList使用columnModelController.columns作为数据,然后使用columnLabel字段显示要导出的实体字段的名称。 源和目标PickList窗口的标题可通过构面进行自定义。 添加导出功能现在我们已经开发了功能选择列表,我们需要对选定的数据进行一些处理。 在本练习中,我们将使用PrimeFaces DataExporter组件提取数据并将其存储到Excel电子表格中。 实际上,我们需要将DataTable合并到视图中以首先显示数据,然后可以使用DataExporter组件导出驻留在表中的数据。 为了构造将用于显示数据的DataTable,我们需要向ColumnModelController类添加一些方法。 这些方法将使我们能够动态地处理DataTable,以便我们可以基于在PickList中选择的列构造列。 实际上,DataTable将查询所有Customer数据,然后仅显示在PickList中选择的那些数据列。 (我们可以通过添加过滤器来修改此查询,但这超出了本文的范围)。 要向表中加载数据,我们只需调用com.acme.acmepools.jsf.CustomerController getItems()方法即可返回所有数据……public List <Customer> getItems(){if(items == null){items = getFacade()。findAll(); } 退换货品; }…现在,我们将必要的方法添加到ColumnModelController,以便我们可以动态构造表。 首先,添加一个单击“导出”按钮将被调用的方法。 此方法将负责构建当前选择的列列表:
public void preProcess(Object document) {
System.out.println("starting preprocess");
updateColumns();
}
接下来,让我们看一下由preProcess()方法调用的updateColumns()的代码:
/**
* Called as preprocessor to export (after clicking Excel icon) to capture
* the table component and call upon createDynamicColumns()
*/
public void updateColumns() {
//reset table state
UIComponent table = FacesContext.getCurrentInstance().getViewRoot().findComponent(":customerExportForm:customerTable");
table.setValueExpression("sortBy", null);
//update columns
createDynamicColumns();
}
updateColumns()方法将UIComponent绑定到JSF视图中的表。 然后,它有能力提供排序(如果选择)。 随后,现在让我们看一下被调用的createDynamicColumns()方法。
private void createDynamicColumns() {
String[] columnKeys = this.getIncludedColumnsByName().split(",");
columns = new ArrayList<>();
for (String columnKey : columnKeys) {
String key = columnKey.trim();
columns.add(new ColumnModel(getColumnLabel(key), key));
}
}
createDynamicColumns()方法可以完成一些工作。 首先,它从PickList中捕获所有选定的列,并将它们存储到名为columnKeys的String []中。 为此,我们使用名为getIncludedColumnsByName()的帮助程序方法,并以逗号分隔结果。 此方法的源如下,它基本上从PickListBean中获取当前选择的列,并将每个列附加到一个String中,然后将其返回给调用者。
public String getIncludedColumnsByName() {
String tempIncludedColString = null;
System.out.println("Number of included columns:" + pickListBean.getColumns().getTarget().size());
List localSource = pickListBean.getColumns().getTarget();
for (int x = 0; x <= localSource.size() - 1; x++) {
String tempModel = (String) localSource.get(x);
if (tempIncludedColString == null) {
tempIncludedColString = tempModel;
} else {
tempIncludedColString = tempIncludedColString + "," + tempModel;
}
}
return tempIncludedColString;
}
接下来,createDynamicColumns()方法然后使用循环解析String []中的每个选定列,并将它们添加到columnList,这将用于使用适当的列构造DataTable。
现在,让我们看一下用于构造DataExport实用程序的标记:
<p:datatable id="customerTable" rendered="false" value="#{customerController.items}" var="item" widgetvar="customerTable">
<p:columns columnindexvar="colIndex" value="#{columnModelController.dynamicColumns}" var="column">
<f:facet name="header">
<h:outputtext value="#{column.header}">
</h:outputtext></f:facet>
<h:outputtext value="#{item[column.property]}">
</h:outputtext></p:columns>
</p:datatable>
<hr />
<h:outputtext value="Type of file to export: ">
<h:commandlink>
<p:graphicimage value="/faces/resources/images/excel.png">
<p:dataexporter filename="customers" id="propertyXlsExport" preprocessor="#{columnModelController.preProcess}" target="customerTable" type="xls">
</p:dataexporter></p:graphicimage></h:commandlink>
</h:outputtext>
如您所见,DataTable设置为不呈现,因为我们确实不希望显示它。 相反,我们希望使用DataExporter组件导出其内容。 为了动态构造DataTable,这些列会调用columnModelController.dynamicColumns方法以返回动态列列表。 该方法如下所示:
public List<ColumnModel> getDynamicColumns() {
return columns;
}
在DataExporter实用程序组件中,将columnModelController.preProcess方法分配给预处理器属性以启动动态列列表。 目标设置为customerTable小部件,该部件是我们根据所选列动态构建的DataTable。 为了将其导出到xls电子表格,必须在项目的Maven POM中添加org.apache.poi依赖项,如下所示:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.7</version>
</dependency>
就是这样……现在您应该拥有使用PrimeFaces组件的功能齐全的数据导出实用程序。 使用以下链接可在GitHub上获得完整的资源。 此代码已用NetBeans IDE 8.0编写,并已部署到GlassFish 4.0。 我在该项目中使用了PrimeFaces 5.0。
- GitHub来源: https : //github.com/juneau001/AcmePools
翻译自: https://www.javacodegeeks.com/2014/12/developing-a-data-export-utility-with-primefaces.html
primefaces