Dorado 5 : a.部门维护 (T1B)

简述

部门维护界面主要用于维护部门信息表,在HSQL数据库中其定义如下:

CREATE MEMORY TABLE DEPT( 
	 DEPT_ID VARCHAR(16) NOT NULL PRIMARY KEY, 
	 BRANCH_ID VARCHAR(16), 
	 DEPT_NAME VARCHAR(30) 
)

该界面代表了单表维护的典型界面展现风格,利用AJAX技术实现增删改查等四种逻辑操作。界面效果如下:

界面的基本功能描述:

  • 上方的AutoForm与DataTable保持联动同步关系;
  • 利用中间的DataPilot可以对页面中的数据作增删改操作,并最终通过"保存所有修改"按钮实现页面无刷新的AJAX提交;
  • 表格中的过滤栏提供了数据筛选功能;
  • 最下方右侧的分页导航栏实现查询结果的数据分页显示功能;

实际上该例的功能也就是dorado提供的视频教程中helloworld的一部分,现在我们自己来实现一次吧。
通过该界面的学习可以通过dorado快速地实现业务系统中各种数据表的单表维护工作。
与<<dorado 5 快速入门(一)>>的区别
在<<dorado 5 快速入门(一)>>中我们通过AutoSqlDataset快速的实现了部门信息的维护工作,在本文中我们将采用另外两种类型的dataset实现部门信息的维护,从中我们可以看到dorado开发中整个表现层的开发与dataset类型是无关的,它们只是在存取数据的持久层设计方面有所差别而以。整个dorado的表现层开发技术可以应用于任意一种dataset,并且不会影响表现层的各种展现能力。
另外我们在本文中也可以了解到dorado的listener技术。以及学会如何使listener与dataset配合工作,更好的响应客户端的AJAX请求,本文的AJAX请求的具体业务就是数据查询,分批数据下载和批量数据保存工作。
期间我们会接触到dorado的flushData以及update等核心技术。从而了解dorado中数据处理技术的关键点。

SqlDataset实现部门维护 (T1B)

创建视图模型(View)

在src/hr目录下创建manage文件夹,并在manage文件夹下新建Dept视图模型对象:

图表 6 1

创建数据集(Dataset)

在新建的视图模型中选中Datasets节点,点左边工具条上的SqlDataset按钮,系统自动生成一个SqlDataset对象:

图表 6 2
把默认生成的Dataset的id属性dataset1修改为datasetDept。
设置Dataset的keyFields属性,值为dept_id,该属性在dataset执行批量数据提交时被使用。

图表 6 3
设置pageSize属性,值为10,通过这个属性,Dataset可以按照每页10条记录来分页。

图表 6 4
设置sql属性,值为"select * from dept"。

图表 6 5
选择datasetDept节点,并利用Auto Create Fields快捷菜单自动生成datasetDept中的字段:

图表 6 6
设置各个字段的label属性如下:

字段

label

DEPT_ID

部门编号

BRANCH_ID

分公司编号

DEPT_NAME

部门名称

到这里数据集datasetDept的设置就完成了。最终dataset的属性设定如下:

图表 6 7

创建数据表格(DataTable)

Dataset设定结束之后,我们再使用数据表格展示dataset的数据。以及通过数据表格的增删改等操作dataset的数据。
在当前的视图模型中选择Controls节点,添加DataTable对象,并选中新建的DataTable对象修改其属性:

属性

dataset

datasetDept

height

100%

id

tableDept

showHScrollBar

false

width

100%

设计视图如下:

图表 6 8

创建分页导航条(PagePilot)

选中Controls节点,添加组件PagePilot,并修改属性如下:

属性

id

pagepilotDept

dataset

datasetDept

设计视图如下:

图表 6 9

创建数据导航条(DataPilot)

选中Controls节点,添加组件DataPilot,并修改属性如下:

属性

id

datapilotDept

dataset

datasetDept

设计视图如下:

图表 6 10

创建数据表单(AutoForm)

选中Controls节点,添加组件AutoForm,并修改属性如下:

属性

id

formDept

dataset

datasetDept

设计视图如下:

图表 6 11
选择formDept节点,点左边工具栏中Auto create elements快捷按钮利用绑定的datasetDept中的字段信息自动生成表单元素。
生成表单元素后,展开formDept节点,并选中自动生成的<FormGroup>节点,设置title属性为"部门信息"。

图表 6 12
设定结束后保存视图模型的所有修改。

创建数据更新命令(UpdateCommand)

选中Controls节点,点左边工具栏中的图标(从上往下数第7个),在列表中选择UpdateCommand。生成后,修改新建命令对象的id为commandSave,并展开commandSave节点,在datasetInfos节点下添加一个新的datasetInfo节点,并设定其dataset属性为datasetDept:

图表 6 13
表示命令执行时自动的将DatasetInfos中包含的datasetDept对象提交到服务器。

创建保存按钮(Button)

设定好数据更新命令之后,由于数据更新命令是不可见对象,为了方便用户操作,我们,还需要添加一个按钮,使用户单击该按钮时自动执行commandSave的默认动作。
选中Controls节点,新增Button对象,并设置其属性如下:

属性

command

commandSave

id

buttonSave

value

保存

width

60

设计视图如下:

图表 6 14

创建JSP

之后在WebRoot目录中新建manage目录,并利用当前部门维护的Dept视图模型的JSP生成向导在manage目录中生成dept.jsp页面。
并利用Layout调整dept.jsp的布局设定,最终代码如下:

<%@ page contentType="text/html; charset=UTF-8" %> 
<%@ taglib uri="http://www.bstek.com/dorado" prefix="d" %> 
 <html> 
 <head> 
 <title></title> 
 </head> 
 <body> 
 <d:View config="hr.manage.Dept"> 
 <d:Layout type="vflow" height="100%"> 
 <d:Pane> 
 <d:AutoForm id="formDept" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Layout type="Hflow"> 
 <d:Pane> 
 <d:DataPilot id="datapilotDept" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Button id="buttonSave" /> 
 </d:Pane> 
 </d:Layout> 
 </d:Pane> 
 <d:Pane height="100%"> 
 <d:DataTable id="tableDept" /> 
 </d:Pane> 
 <d:Pane align="right"> 
 <d:PagePilot id="pagepilotDept" /> 
 </d:Pane> 
 </d:Layout> 
 </d:View> 
 </body> 
 </html>

我们在主框架界面的菜单对象初始化的时候(MainViewModel.java中的initControls方法)已经设定好了相关菜单项的链接功能,代码如下:

subItem = item.addItem("dept", "部门信息"); 
subItem.setPath("manage/dept.jsp");

上面的菜单项的path属性定义为"manage/dept.jsp"。

查看运行效果

下面我们通过主框架查看效果。在主框架中选择部门信息菜单项。
最终效果:

图表 6 15
现在让我们验证一下部门维护界面的基本功能:
AutoForm与表格的数据联动功能:你可以选择表格中的不同行,注意观察AutoForm中的数据是否和表格中的当前行保持一致;你也可以修改表格中的数据并注意观察AutoForm中的数据是否能保持一致;
批量数据更新:利用DataPilot对表格进行增删改的操作,并最终单击"保存所有修改"按钮完成批量提交;提交后刷新页面验证提交是否成功;
分页导航条的翻页功能;

功能改进一:分公司信息下拉框维护

当编辑分公司字段时,我们希望看到分公司的名称信息,并可以通过选择分公司的下拉列表进行维护,这样可以避免用户的误输入。如下图维护部门所属分公司时,可以通过一个分公司下拉框列表供用户选择:

图表 6 16
该处采用了dorado中的DropDown(下拉框)开发技术。通常情况下DropDown中的数据我们都可以比较方便的在View中的DropDown对象中直接配置,如:

通过第五章的学习我们已经知道DropDown对象我们也可以在视图模型中用Java代码中初始化。
下面我们学习另一种类型的DropDown对象,该对象其中的列表项来自与一个Dataset对象。使用时我们只要创建一个DatasetDropDown就可以实现这个功能,顾名思义,就是通过Dataset获取数据并作为下拉框的数据项使用。
下面我们就利用DatasetDropDown对象实现分公司名称的处理。
在我们创建DatasetDropDown之前,先创建一个SqlDataset,其基本属性的配置如下:

属性

id

datasetBranch

sql

select * from branch

利用SqlDataset的Auto Create Fields快捷按钮自动生成字段,如下图:

图表 6 17
具体设计步骤请参考前面的datasetDept创建过程,这里不再描述。
创建好datasetBranch之后,我们选择Controls节点,再创建一个DatasetDropDown,属性设置如下:

属性

dataset

datasetBranch

labelField

BRANCH_NAME

valueField

BRANCH_ID

mapValue

true

设计视图如下:

图表 6 18
最后,我们将dropdownBranch绑定到branch_id字段上。
选中datasetDept的BRANCH_ID字段,设置dropDown属性,选择dropdownBranch。

图表 6 19
好了,经过上面一系列步骤,基本完成了下拉框的设计。
保存所有的修改,并重新浏览该页面,可以看到如下的效果:

图表 6 20
现在我们已经可以利用dropdownBranch提供的下拉功能修改部门的所属分公司信息了。

功能改进二:提供查询功能

该部门维护页面中已经提供了增加,删除和修改等比较全面的维护功能。下面我们继续增强该页面的基本功能:增加查询功能。

定义监听器对象

由于采用了SqlDataset,其查询语句是由我们在Dept视图模型中的sql属性定义的,同第五章提到动态组件技术一样我们也可以通过java代码动态的设定和修改SqlDataset的属性(当然对于其他类型的dataset也一样)。
下面我们利用dataset的listener机制实现SqlDataset查询sql语句的动态修改。
Listener也叫监听器,用于监听Dataset中的各种事件,在某些书籍中的术语叫回调机制。
在AJAX应用中监听器的作用更像是一个服务器端的服务接口。随时为dataset的数据请求和数据保存等各种动作提供服务。
下面我们利用dorado studio的listener向导为datasetDept创建一个listener。

图表 6 21
选择datasetDept对象,并在左侧找到 图标,单击打开监听器创建向导:

图表 6 22
在向导视图中保持默认的Class Name名称和Package不变,同时选中Options中的beforeLoadData数据行,覆盖其中的beforeLoadData事件。自动生成的java代码如下:

package hr.manage;
import com.bstek.dorado.data.*;
import com.bstek.dorado.common.*;
/**
 * Dept_datasetDeptListener
 */
public class Dept_datasetDeptListener extends AbstractDatasetListener {
    public boolean beforeLoadData(Dataset dataset) throws Exception {
        return true;
    }
}

向导关闭的同时会自动的设定datasetDept的listener属性为刚才新建的Listener,注意看下图的listener属性:

图表 6 23
这样新建listener与datasetDept关联上了,在datasetDept利用其sql属性加载数据之前会自动得触发listener中的beforeLoadData方法。注意看beforeLoadData方法:

public boolean beforeLoadData(Dataset dataset){}

其中包含了执行数据加载动作的datasetDept对象,即方法中的dataset参数。则我们就可以在这儿动态修改dataset的sql属性。并在beforeLoadData方法执行结束之后,datasetDept执行查询时所用的sql就是已经改变后的sql语句了。通过这种方式我们来实现dataset的条件查询功能。

public boolean beforeLoadData(Dataset dataset) throws Exception { 
         String deptId = dataset.parameters().getString("dept_id"); 
         String branchId = dataset.parameters().getString("branch_id"); 
         String deptName = dataset.parameters().getString("dept_name"); 
         
         String sql = "select * from dept"; 
         String where = ""; 
         if (StringHelper.isNotEmpty(deptId)){ 
         	where = " (dept.dept_id like '%"+deptId"%')"; 
         } 
         if (StringHelper.isNotEmpty(branchId)){ 
         	where = ((StringHelper.isEmpty(where))?" ":" and ")" (dept.branch_id like '%"branchId"%')"; 
         } 
         if (StringHelper.isNotEmpty(deptName)){ 
         	where = ((StringHelper.isEmpty(where))?" ":" and ")" (dept.dept_name like '%"deptName"%')"; 
         } 
         
         SqlDataset sqlDataset = (SqlDataset)dataset; 
         sqlDataset.setSql(sql + ((StringHelper.isEmpty(where))?" ":" where ") + where); 
         return true; 
 }

其中dataset的parameters集合为dataset的一个参数集合,在dataset执行查询的AJAX请求时,该参数的值将由客户端上传上来(下一步我们将会继续讨论这些值的来源)。我们将根据参数值动态产生一个查询SQL语句,并最终修改datasetDept的sql属性。
注意beforeLoadData方法的最后一行代码return true。这是beforeLoadData方法的一种处理机制,该方法通过返回值决定是否由dataset执行默认的数据加载工作,本例就是决定datasetDept是否还要用其sql属性执行查询。
通过以上的Listener的beforeLoadData方法的定义,我们已经可以使datasetDept支持查询处理了,可以说万事俱备只差客户端的AJAX查询请求了。

修改界面以便发出查询请求

下一步我们要做的就是给dataset的parameters赋值,并利用dataset的flushData并向服务器重新请求数据。flushData执行时会自动的将parameters中的值传递到服务器的监听器中。
flushData方法的基本说明如下:
flushData是dataset提供的客户端方法,所有dataset都具有该方法。该方法的主要作用是重新从服务器端下载数据集的数据来填充当前dataset。执行此方法时dataset中原有的记录将被全部清除。
在客户端可以直接调用flushData()方法向服务器发出请求:

/范例1:范例说明:利用dataset的flushData()方法,上传dept_id参数信息,并重新获取数据/
dataset.parameters().setValue("dept_id", "D11");dataset.flushData();

更详细的内容请参考<<dorado 5 用户指南 v1.1 .doc>>。
上述代码范例中的参数值是通过代码写在程序中的,我们的范例希望由用户可以直接在界面上输入查询条件,并单击查询按钮得到查询结果。下面我们就按照这种方式实现datasetDept的查询功能。
界面的基本风格如下:

图表 6 24
上图的最上方区域为用户输入查询条件的区域,并最终单击查询按钮实现部门信息的查询功能。
设计步骤如下:
步骤一:Dept视图模型中添加新的FormDataset,并命名为datasetCondition。同时新增三个字段属性设置如下图:

图表 6 25
步骤二:添加AutoForm对象formQuery,并绑定到datasetCondition上,如下:

图表 6 26
步骤三:新增Button对象buttonQuery,属性设置如下:

属性

id

buttonQuery

value

查询

width

60

另外我们要在buttonQuery的单击事件中获取datasetCondition中的查询参数值并通过dataset的parameters传递到服务器执行查询。
将当前视图切换到Events Inspector面板,并选择buttonQuery的onClick事件,在其中输入代码:

var params = datasetDept.parameters(); 
params.setValue("dept_id", datasetCondition.getValue("dept_id")); 
params.setValue("branch_id", datasetCondition.getValue("branch_id")); 
params.setValue("dept_name", datasetCondition.getValue("dept_name")); 
datasetDept.flushData();

代码实现的功能就是从datasetCondition中取出值,并储存在datasetDept的parameters中,再调用flushData方法发送到服务器。执行查询动作,这时候会触发服务器端listener的beforeLoadData方法,并最终获得查询后的数据。
步骤四:利用AutoForm的Auto create elements快捷按钮,导入datasetCondition的字段信息。并设定<FormGroup>的title属性为 "查询条件",以及利用AutoForm的Custom Element对象将buttonQuery引入到formQuery中。如下图:

图表 6 27
步骤五:在dept.jsp上修改代码,将新增的formQuery添加到JSP中:

<d:Layout type="vflow" height="100%">
<d:Pane>
<d:AutoForm id="formQuery" />
</d:Pane>
<d:Pane>
<d:AutoForm id="formDept" />
</d:Pane>
...

其中的蓝色代码为新增内容
保存所有修改。
(查询设置完成)
体验查询结果
刷新页面,在查询条件中输入部门编号为"D1",并单击查询按钮:

图表 6 28

CustomDataset实现部门维护 (T1B)

创建视图模型(View)

在src/hr目录manage文件夹下新建Dept1视图模型对象:

图表 6 29

创建数据集(Dataset)

新建CustomDataset

在新建的视图模型中选中Datasets节点,点左边工具条上的CustomDataset按钮,系统自动生成一个CustomDataset对象:

图表 6 30
把默认生成的Dataset的id属性dataset1修改为datasetDept。
设置pageSize属性,值为10,通过这个属性,Dataset可以按照每页10条记录来分页。

图表 6 31

知识预备:Dataset与POJO的数据转换功能

Dorado中的各种Dataset都提供了一些方法,用于初始化dataset的数据,例如:

dataset.insertRecord(); 
dataset.setString("degree", "高中"); 
dataset.insertRecord(); 
dataset.setString("degree", "大专"); 
dataset.insertRecord(); 
dataset.setString("degree", "大学"); 
dataset.insertRecord(); 
dataset.setString("degree", "硕士"); 
dataset.insertRecord(); 
dataset.setString("degree", "博士"); 
dataset.insertRecord(); 
dataset.setString("degree", "其它");

以上代码就是利用dataset的insertRecord()方法插入一条空纪录,并利用set方法设置dataset中相关字段的值,set方法可以支持多种类型的数据,例如int,string,float,double,date等等。
对于CustomDataset我们就可以利用这种方法实现dataset的初始化。
但是这种使用方式比较的费劲。
因此dorado中的dataset提供了与POJO对象的转换功能,使用时我们只需要将dataset与需要转换的POJO对象设着好映射关系。再通过dataset与POJO的转换方法实现POJO数据转换为dataset的数据,同样也能实现dataset中的数据转换为POJO的数据。
一下我们就以本例的实现详细说明。

建立datasetDept与Dept.java的映射关系

在src/hr/manage目录中新增目录domain。用以存放本文开发中要使用的POJO对象。
在domain目录下我们增加一个Dept.java对象,基本的设定如下:

Dept.java

package hr.manage.domain;
public class Dept {
    private String deptId;
    private String branchId;
    private String deptName;
    public String getBranchId() {
        return branchId;
    }
    public void setBranchId(String branchId) {
        this.branchId = branchId;
    }
    public String getDeptId() {
        return deptId;
    }
    public void setDeptId(String deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
}

设定datasetDept的objectClazz为"hr.manage.domain.Dept"。
并在datasetDept中新增三个字段,分别为:
deptId,属性设置如下:

属性

dataType

string

label

部门编号

name

deptId

property

deptId

branchId,属性设置如下:

属性

dataType

string

label

分公司编号

name

branchId

property

branchId

deptName,属性设置如下:

属性

dataType

string

label

部门名称

name

deptName

property

deptName

其中的property属性与datasetDept的objectClazz属性对应的POJO对象的属性相对应,例如deptId字段的定义就表示它与Dept.java中的deptId属性相对应。
另外Field与POJO的属性映射还存在一种默认的约定,如以上三个字段的设定中name与Dept中的属性名称完全相同,则我们可以在Field的设置中省略property属性的设定。只有在Field的名称与POJO不一致时,我们才有必要设定Field的property属性,去处property属性设置后,datasetDept的设计视图就如下:

图表 6 32

创建数据表格(DataTable)

6.1.2.3

创建分页导航条(PagePilot)

6.1.2.4

创建数据导航条(DataPilot)

6.1.2.5

创建数据表单(AutoForm)

6.1.2.6

创建数据更新命令(UpdateCommand)

6.1.2.7

创建保存按钮(Button)

6.1.2.8

创建JSP

之后在WebRoot目录中新建manage目录,并利用当前部门维护的Dept视图模型的JSP生成向导在manage目录中生成dept1.jsp页面。
并利用Layout调整dept1.jsp的布局设定,最终代码如下:

<%@ page contentType="text/html; charset=UTF-8" %> 
<%@ taglib uri="http://www.bstek.com/dorado" prefix="d" %> 
 <html> 
 <head> 
 <title></title> 
 </head> 
 <body> 
 <d:View config="hr.manage.Dept1"> 
 <d:Layout type="vflow" height="100%"> 
 <d:Pane> 
 <d:AutoForm id="formDept" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Layout type="Hflow"> 
 <d:Pane> 
 <d:DataPilot id="datapilotDept" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Button id="buttonSave" /> 
 </d:Pane> 
 </d:Layout> 
 </d:Pane> 
 <d:Pane height="100%"> 
 <d:DataTable id="tableDept" /> 
 </d:Pane> 
 <d:Pane align="right"> 
 <d:PagePilot id="pagepilotDept" /> 
 </d:Pane> 
 </d:Layout> 
 </d:View> 
 </body> 
 </html>

我们在主框架界面的菜单对象初始化方法中添加新的菜单项,在MainViewModel.java的initControls方法中加入代码,代码如下:

subItem = item.addItem("dept", "部门信息");
subItem.setPath("manage/dept.jsp");
subItem = item.addItem("dept1", "部门信息1");
subItem.setPath("manage/dept1.jsp");
...

上面的蓝色代码为新插入的代码,用以关联刚才新建的dept1.jsp页面。

查看运行效果

下面我们通过主框架查看效果。在主框架中选择部门信息1菜单项。
最终效果:

图表 6 33
在以上的界面中还没有看到部门数据。下一步我们将初步完善该页面的基本功能。

功能改进一:添加Listener实现数据查询,并实现查询结果Dept与datasetDept的数据转换

监听器的创建方法参考上文SqlDataset功能改进二中的办法。
在监听器的创建向导中:

图表 6 34
勾选afterLoadData方法,afterLoadData方法是Dataset执行数据加载后会自动调用的方法。该方法调用时,会将dataset作为参数传入,我们希望在该方法内部初始化datasetDept的数据。
在向导视图中保持默认的Class Name名称和Package不变。自动生成的java代码如下:

package hr.manage;
import com.bstek.dorado.data.*;
import com.bstek.dorado.common.*;
/**
 * Dept1_datasetDeptListener
 */
public class Dept1_datasetDeptListener extends AbstractDatasetListener {
    public void afterLoadData(Dataset dataset) throws Exception {
    }
}

向导关闭的同时会自动的设定datasetDept1的listener属性为刚才新建的Listener,注意看下图的listener属性:

图表 6 35
这样新建listener与datasetDept关联上了。

利用listener实现数据导入功能

注意看afterLoadData方法:

public void afterLoadData(Dataset dataset){}

其中包含了执行数据加载动作的datasetDept对象,即方法中的dataset参数。则我们就可以在这儿将查询出的Dept对象与datasetDept转换。将数据交给dataset。

public void afterLoadData(Dataset dataset) 
 throws Exception { 
	 DeptDao dao = new DeptDao(); 
	 List deptList = dao.getAll(); 
	 dataset.fromDO(deptList); 
 }

我们通过一个自定义的DeptDao对象获取一个部门列表信息,并通过dataset的fromDO将部门列表转化到dataset中。
Dataset的fromDO方法是dataset自动集成的方法,该方法的说明如下:

自动利用反射从传入的data参数指定的对象(本例就是指deptList对象)中读取数据, 并追加到Dataset中.
此处的data可以是单个的Bean、java.util.Map的派生类、java.util.Collection的派生类 或对象数组.此方法会自行根据data的类型决定处理方式.
对于Map派生类,Collection的派生类和对象数组要求其中包含的都为dataset的objectClazz属性指定的Java Bean。

上述代码中使用的DeptDao对象的相关代码参考附录一
现在来重新浏览刚才的JSP页面,可以看到如下的界面:

图表 6 36

利用listener实现数据保存功能

在listener中再添加一个beforeUpdate方法的声明。

public boolean beforeUpdateData(Dataset dataset) throws Exception {}

该方法在dataset执行数据更新时触发,参数dataset在本例中就是datasetDept。由于dataset是一个包含多行数据的大对象,并且在客户端用户可能对其中的数据作不同的操作,新增的记录,修改的记录,删除的记录(被删除的记录在dataset中只是做一个删除标记)。这些数据的修改都是只在用户通过UpdateCommand提交到服务器端的时候,才会把所有的信息提交到Server端。
则我们在Listener中,就需要遍历dataset中的数据,在本例子中遍历dataset中的数据之前,我们通过如下的代码先做一个过滤:

RecordIterator ri = dataset.recordIterator();
ri.setVisibility(Dataset.FILTER_CHANGED);

RecordIterator是dataset提供的一个遍历记录迭代器,由于在本例中我们只关心dataset中发生修改过的记录。因此我们在遍历dataset中的记录时,先设置过滤方式为"Dataset.FILTER_CHANGED"。该种过滤方式可以遍历到所有发生变化的记录(包含新添加的、被修改过的和已删除的)。
记录的修改,新增和删除状态我们可以通过record的getState方法加以区别,主要类型有:

记录状态

说明

STATE_DELETED

已删除

STATE_MODIFIED

已更新

STATE_NEW

新增

STATE_NONE

无状态.数据没有发生任何的修改。此状态为记录的默认状态.

通过以上的几个概念,最终我们修改beforeUpdateData方法的最终代码为:

public boolean beforeUpdateData(Dataset dataset) throws Exception {
        DeptDao dao = new DeptDao();
        RecordIterator ri = dataset.recordIterator();
        ri.setVisibility(Dataset.FILTER_CHANGED);
        while (ri.hasNext()) {
            Record record = ri.nextRecord();
            Dept dept = (Dept) record.toDO();
            switch (record.getState()) {
            case Record.STATE_NEW:
                dao.save(dept);
                break;
            case Record.STATE_MODIFIED:
                dao.update(dept);
                break;
            case Record.STATE_DELETED:
                dao.remove(dept);
                break;
            default:
            }
        }
        return true;
}

代码中根据记录的不同状态,调用DeptDao的不同持久化方法实现数据保存和删除工作。
本例中DeptDao中并没有实现save(),update(),remove()方法,请读者自行实现,该部分的内容已经超过了表现层技术范围,不再详细讨论。

功能改进二:分公司信息下拉框维护

当编辑分公司字段时,我们希望看到分公司的名称信息,并可以通过选择分公司的下拉列表进行维护,这样可以避免用户的误输入。如下图维护部门所属分公司时,可以通过一个分公司下拉框列表供用户选择:

图表 6 37
该处采用了dorado中的DropDown(下拉框)开发技术。通常情况下DropDown中的数据我们都可以比较方便得在View中的DropDown对象中直接配置,如:

通过第五章的学习我们已经知道DropDown对象我们也可以在视图模型中用Java代码中初始化。
下面我们学习另一种类型的DropDown对象,该对象其中的列表项来自与一个Dataset对象。使用时我们只要创建一个DatasetDropDown就可以实现这个想法,顾名思义,就是通过Dataset获取数据并作为下拉框的数据项使用。
下面我们就利用DatasetDropDown对象实现分公司名称的处理。
在本例中我们依然采用CustomDataset实现部门下拉列表(当然你也可以使用AutoSqlDataset,SqlDataset实现,这两种方式在初级培训教程以及本手册的前面都已经有详细说明)在我们创建DatasetDropDown之前,先创建一个CustomDataset,并命名为datasetBranch。

建立datasetBranch与Branch.java的映射关系

在src/hr/manage/domain。用以存放本文开发中要使用的POJO对象。
在domain目录下我们增加一个Branch.java对象,基本的设定如下:

Branch.java

package hr.manage.domain;
public class Branch {
    private String branchId;
    private String branchName;
    public String getBranchId() {
        return branchId;
    }
    public void setBranchId(String branchId) {
        this.branchId = branchId;
    }
    public String getBranchName() {
        return branchName;
    }
    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }
}

设定datasetBranch的objectClazz为"hr.manage.domain.Branch"。
并在datasetBranch中新增两个字段,分别为:
branchId,属性设置如下:

属性

dataType

string

label

分公司编号

name

branchId

property

branchId

branchName,属性设置如下:

属性

dataType

string

label

分公司名称

name

branchName

property

branchName

其中的property属性与datasetBranch的objectClazz属性对应的POJO对象的属性相对应,例如branchId字段的定义就表示它与Branch.java中的branchId属性相对应。
另外Field与POJO的属性映射还存在一种默认的约定,如以上两个字段的设定中name与Branch中的属性名称完全相同,则我们可以在Field的设置中省略property属性的设定。只有在Field的名称与POJO不一致时,我们才有必要设定Field的property属性,去处property属性设置后,datasetBranch的设计视图就如下:

图表 6 38

建立DatasetDropDown

创建好datasetBranch之后,我们选择Controls节点,再创建一个DatasetDropDown,属性设置如下:

属性

dataset

datasetBranch

labelField

branchName

valueField

branchId

mapValue

true

设计视图如下:

图表 6 39
最后,我们将dropdownBranch绑定到branch_id字段上。
选中datasetDept的branch_id字段,设置dropDown属性,选择dropdownBranch。

图表 6 40
好了,经过上面一系列步骤,基本完成了下拉框的设计。

利用Listener中实现datasetBranch的数据导入工作

利用datasetBranch的listener的向导创建功能,新增Listener:

图表 6 41
勾选其中的afterLoadData方法,确保datasetBranch的listener连接到新建的Listener:

图表 6 42
注意看afterLoadData方法:

public void afterLoadData(Dataset dataset){}

其中包含了执行数据加载动作的datasetBranch对象,即方法中的dataset参数。则我们就可以在这儿将查询出的Branch对象与datasetBranch转换。将数据交给dataset。

public void afterLoadData(Dataset dataset) 
 throws Exception { 
	 BranchDao dao = new BranchDao(); 
	 List branchList = dao.getAll(); 
	 dataset.fromDO(branchList); 
 }

我们通过一个自定义的BranchDao对象获取一个部门列表信息,并通过dataset的fromDO将部门列表转化到dataset中。
Dataset的fromDO方法是dataset自动集成的方法,该方法的说明如下:

自动利用反射从传入的data参数指定的对象(本例就是指branchList对象)中读取数据, 并追加到Dataset中.
此处的data可以是单个的Bean、java.util.Map的派生类、java.util.Collection的派生类 或对象数组.此方法会自行根据data的类型决定处理方式.
对于Map派生类,Collection的派生类和对象数组要求其中包含的都为dataset的objectClazz属性指定的Java Bean。

上述代码中使用的BranchDao对象的相关代码参考附录一
保存所有的修改,并重新浏览该页面,可以看到如下的效果:

图表 6 43
现在我们已经可以利用dropdownBranch提供的下拉功能修改部门的所属分公司信息了。

功能改进三:提供查询功能

该部门维护页面中已经提供了增加,删除和修改等比较全面的维护功能。下面我们继续增强该页面的基本功能:增加查询功能。

修改afterLoadData方法

在前面我们已经定义了datasetDept的listener对象,其中通过DeptDao对象执行数据加载动作。为了实现查询,我们就可以在DeptDao中添加一个新的方法,使得它可以支持数据查询。

public List getAll(ParameterSet parameters) throws Exception {}

代码如下:

public List getAll(ParameterSet parameters) throws Exception {
        String deptId = parameters.getString("deptId");
        String branchId = parameters.getString("branchId");
        String deptName = parameters.getString("deptName");
        List result = new ArrayList();
        DBStatement stmt = this.getStatement(DBStatement.SELECT, "dept");
        try {
            ParameterSet p = stmt.parameters();
            if (StringHelper.isNotEmpty(deptId)) {
                p.setString("dept_id", deptId);
            }
            if (StringHelper.isNotEmpty(branchId)) {
                p.setString("branch_id", branchId);
            }
            if (StringHelper.isNotEmpty(deptName)) {
                p.setString("dept_name", deptName);
            }
            List list = stmt.queryForList();
            for (int i = 0; i < list.size(); i++) {
                VariantSet entity = (VariantSet) list.get;
                result.add(this.convert(entity));
            }
        } finally {
            stmt.close();
        }
        return result;
}

修改Dept1_datasetDeptListener.java中的afterLoadData方法:

public void afterLoadData(Dataset dataset) throws Exception { 
	 DeptDao dao = new DeptDao(); 
	 List deptList = dao.getAll(dataset.parameters()); 
	 dataset.fromDO(deptList); 
 }

其中dataset的parameters集合为dataset的一个参数集合,在dataset执行查询的AJAX请求时,该参数的值将由客户端上传上来(下一步我们将会继续讨论这些值的来源)。我们将参数传递到DeptDao的getAll方法中,并由该方法的内部负责解析和执行查询。
通过以上的Listener的afterLoadData方法的定义,我们已经可以使datasetDept支持查询处理了,可以说万事俱备只差客户端的AJAX查询请求了。

修改界面以便发出查询请求

下一步我们要做的就是给dataset的parameters赋值,并利用dataset的flushData并向服务器重新请求数据。flushData执行时会自动的将parameters中的值传递到服务器的监听器中。
flushData方法的基本说明如下:
flushData是dataset提供的客户端方法,所有dataset都具有该方法。该方法的主要作用是重新从服务器端下载数据集的数据来填充当前dataset。执行此方法时dataset中原有的记录将被全部清除。
在客户端可以直接调用flushData()方法向服务器发出请求:

/范例1:范例说明:利用dataset的flushData()方法,上传dept_id参数信息,并重新获取数据/dataset.parameters().setValue("dept_id", "D11");dataset.flushData();

更详细的内容请参考<<dorado 5 用户指南 v1.1 .doc>>。
上述代码范例中的参数值是通过代码写在程序中的,我们的范例希望由用户可以直接在界面上输入查询条件,并单击查询按钮得到查询结果。下面我们就按照这种方式实现datasetDept的查询功能。
界面的基本风格如下:

图表 6 44
上图的最上方区域为用户输入查询条件的区域,并最终单击查询按钮实现部门信息的查询功能。
设计步骤如下:
步骤一:Dept视图模型中添加新的FormDataset,并命名为datasetCondition。同时新增三个字段属性设置如下图:

图表 6 45
步骤二:添加AutoForm对象formQuery,并绑定到datasetCondition上,如下:

图表 6 46
步骤三:新增Button对象buttonQuery,属性设置如下:

属性

id

buttonQuery

value

查询

width

60

另外我们要在buttonQuery的单击事件中获取datasetCondition中的查询参数值并通过dataset的parameters传递到服务器执行查询。
按照上一节SqlDataset实现dept信息维护的做法,我们可以定义buttonQuery的onClick事件,在其中输入代码:

var params = datasetDept.parameters(); 
params.setValue("deptId", datasetCondition.getValue("deptId")); 
params.setValue("branchId", datasetCondition.getValue("branchId")); 
params.setValue("deptName", datasetCondition.getValue("deptName")); 
datasetDept.flushData();

代码实现的功能就是从datasetCondition中取出值,并储存在datasetDept的parameters中,再调用flushData方法发送到服务器。执行查询动作,这时候会触发服务器端listener的beforeLoadData方法,并最终获得查询后的数据。
不过这一次我们通过QueryCommand对象避免这些JS编程,方式如下:
新建QueryCommand对象,命名为commandQuery,并设定conditionDataset为datasetCondition。queryDataset属性设定为datasetDept。如下图:

图表 6 47
并将buttonQuery的command属性设定为commandQuery。这样我们就不必再定义buttonQuery的onClick事件了,通过commandQuery自动的帮我们完成以上的工作。
步骤四:利用AutoForm的Auto create elements快捷按钮,导入datasetCondition的字段信息。并利用AutoForm的Custom Element对象将buttonQuery引入到formQuery中。如下图:

图表 6 48
步骤五:在dept.jsp上修改代码,将新增的formQuery添加到JSP中:

<d:Layout type="vflow" height="100%">
<d:Pane>
<d:AutoForm id="formQuery" />
</d:Pane>
<d:Pane>
<d:AutoForm id="formDept" />
</d:Pane>
...

其中的蓝色代码为新增内容
保存所有修改。
(查询设置完成)
体验查询结果
重新启动服务后,刷新页面,在查询条件中输入分公司编号为"D1",并单击查询按钮:

图表 6 49

功能改进四:实现查询分页

由于浏览器本身的限制以及性能上的考虑,构建与BS的系统都需要提供查询结果使用分页的方式显示数据,如下图:

图表 6 50
用户需要使用翻页的方式浏览查询结果集合。
每一次翻页时,必须明确的告诉Server当前请求的是哪一页的数据,在本文中我们称pageIndex,另外还需要告诉Server,请求pageSize信息,便于Server根据这两个参数计算出符合条件的数据并返回到client端。
关于分页处理,dorado提供的所有的dataset都提供了AJAX技术的翻页方法,用以查询和翻页处理。
如上图中我们单击pagepilotDept中的第二页时,系统底层调用的JS代码为:

datasetDept.setPageIndex(2); 
datasetDept.flushData();

其中的flushData()方法会自动清除dataset中已经下载的所有数据,再向server请求新的数据。
如果想保留已经下载的数据,则我们可以使用:

datasetDept.setPageIndex(2); 
datasetDept.loadData("end");

则datasetDept会保留已经下载到客户端的数据,并且将向Server请求第二页的数据,并附加到dataset当前所有记录的最后面。
当然我们也可以比较直接的用如下的方法去下载Server指定页的数据:

datasetDept.loadPage(2);

dataset的AJAX翻页请求发送到Server时,都会同时发送dataset的pageIndex,pageSize信息。
在初级教程的开发中,以及本例使用SqlDataset维护部门信息的开发过程中,我们只是配置了datasetDept的Sql属性和pageSize属性,组件pagePilot就能自动的计算分页,以及我们可以通过pagePilotDept对象进行数据的翻页操作。在实现这个功能时,我们并没有做什么特别的工作,但是datasetDept就会自动的计算,什么原因呢?那是它们都继承自DBDataset,DBDataset内部已经默认提供了分页处理功能,会自动的根据客户端的翻页请求,将获得的pageSize,pageIndex信息执行数据查询。
本例我们通过CustomDataset实现分页处理,借此了解dorado的分页处理技术。
第一步:在Dept1_datasetDeptListener的afterLoadData方法中获取pageIndex和pageSize的值

public void afterLoadData(Dataset dataset) throws Exception {
int pageIndex = dataset.getPageIndex();
int pageSize = dataset.getPageSize();

DeptDao dao = new DeptDao();
List deptList = dao.getAll(dataset.parameters());
dataset.fromDO(deptList);
}

其中蓝色代码中我们通过dataset的两个方法获取client端分页请求的pageSize与pageIndex属性。
我们可以利用pageIndex与pageSize计算出deptList中符合条件的数据,并转换到datasetDept中,读者你可以自行实现。为了方便开发,dorado中还提供了一个PagingHelper工具类,帮助你实现计算。以上的代码通过PagingHelper进行分页处理后,代码如下:

public void afterLoadData(Dataset dataset) throws Exception {
        int pageIndex = dataset.getPageIndex();
        int pageSize = dataset.getPageSize();
        DeptDao dao = new DeptDao();
        List deptList = dao.getAll(dataset.parameters());
        int rowCount = deptList.size();
        // 利用PagingHelper辅助对List的分页
        PagingHelper paging = new PagingHelper(pageSize, pageIndex, rowCount);
        List depts = deptList.subList(paging.getFromIndex(),
                paging.getToIndex());
        dataset.fromDO(depts); // 将depts中的数据反射到Dataset中
        dataset.setPageCount(paging.getPageCount()); // 设置Dataset的总页数
}

取出deptList中的部分数据,并交给datasetDept,之后还记得要设置dataset的pageCount信息。告诉dataset总页数信息。
保存所有的修改,并确保更新后的java类被重新编译过。再次刷新页面测试pagePilotDept的使用,可以发现它已经可以实现翻页操作了。你还可以调整Dept1视图模型中的datasetDept的pageSize属性,再次刷新页面后查看运行效果。

Attachments:

worddave0f8e5098c0284845771fdcd3274740f.png (image/png)
worddava0c7cdabee54c9fe1c60468d1a6dba8e.png (image/png)
worddav54c744c0098dbd63c9a5b02ed37f1434.png (image/png)
worddave1162de20ee5a7db992f636ef43ce1d0.png (image/png)
worddav5895a607179e5a06d0ee3e92f1a7a2dc.png (image/png)
worddavb50b6902cf0d011dc0e086bb2ec12033.png (image/png)
worddava33f25af5cb6c597cdd703883863d87f.png (image/png)
worddav12f9834c5f59158dc7c303e2456b7abf.png (image/png)
worddav1cc51f4d85625cd2c0615feb8ef29722.png (image/png)
worddav3f6d4b69d127283d95fbcb051c55b3c2.png (image/png)
worddav5ba105f0f53af8802044d7a12886ff42.png (image/png)
worddavcf503b71c9e15c1cf952de1407676d6b.png (image/png)
worddav5afe996368b9b528d89466df02f818a4.png (image/png)
worddav3b19019d47d584e12fe1794988895ed8.png (image/png)
worddav24cebf4ec0cec8b08336313738a14d0e.png (image/png)
worddav16a7784e69d8bd5f1543696a145fe595.png (image/png)
worddav091016eb8b1f09896476b7bcdc114ba5.png (image/png)
worddav99672ebbc5b59828e81bde303d046cc7.png (image/png)
worddava1ef1b1e71cc1cf183c10e21a462f30f.png (image/png)
worddavc80883f1d6a0a71f8aa7ef8e6b2a04d6.png (image/png)
worddav03d959dccf6b170385e262c7966b006c.png (image/png)
worddavfca4d88fa5624dee0179b3b656ffcfa4.png (image/png)
worddavb733ceed2cd4948523b5e83259d674f0.png (image/png)
worddav875eccfefe4fa84d15a56073aa436664.png (image/png)
worddave0365110cb70a897455632dc51e63192.png (image/png)
worddavfcf3cf38a69fe7bcd1a3f06d5c873404.png (image/png)
worddav1966ab4c873a3c0938743184a4cde6cd.png (image/png)
worddav95a3576e0989590ea4bdba478d01d9d4.png (image/png)
worddavf74262a6b355f0b00577d9e58f8df65f.png (image/png)
worddav1d229334dd6c3cf7b9afd86f234bb490.png (image/png)
worddav68394f5545d6f3d6e71d5c9c6cf8bf0b.png (image/png)
worddav5a01b23b0e7b96ff01aaf808a892bfb2.png (image/png)
worddav84c8010b8107325d1428b98787a2e43d.png (image/png)
worddav14a39bf1eeb74e3ba9ee2f7054d5be2b.png (image/png)
worddav7931df40ded38b2d530bbe0f60e2a929.png (image/png)
worddav00290710a228625fe1f95b4350f13350.png (image/png)
worddav776c9d698b119196c95dcc61f1aad872.png (image/png)
worddavb4638b70a15783f8dc8d39929f68dbed.png (image/png)
worddav5adaf21da0ce462bbe62b12cddf9d3bc.png (image/png)
worddav4924838c13391756065a4f9dd1c164bc.png (image/png)
worddav7aca168dcf9413de6a84cab0522ddbce.png (image/png)
worddav030de4c636f7674902117b164937faef.png (image/png)
worddavea89693410613b80603eeee14535e16f.png (image/png)
worddavace1a528e2f007342b909ae37a2b39d1.png (image/png)
worddav47d8195fd6462da4eb657756d6bb57ad.png (image/png)
worddavc149b49517d93a329ec42d35015e9f7f.png (image/png)
worddav4f72953565d6943cab8ae5d95e5b83ee.png (image/png)
worddava135194835cd8e971a5efbf24f8936e8.png (image/png)
worddavdc30bf4c84c4359769fa03c0893d46fc.png (image/png)