Dorado 5 : 1.3.1.QueryCommand(查询命令) (RF2)

简介

系统中我们经常能看到这种类型的页面

图表 11 Query Window
页面上共分两个区域:查询条件输入区域与查询结果显示区域。
看看查询功能实现的基本步骤:

  • 上传查询条件
  • Dao接受查询条件并实现查询
  • 返回结果到客户端

QueryCommand主要用于完成第一步的功能.同时利用dorado的AJAX处理技术,QueryCommand也支持页面无刷新的方式将查询条件上传到服务器,并利用dataset的flushData技术得到查询结果。这也是我们推荐的方式,可以在很大程度上加快页面反应速度以及杜绝中文参数传递问题。

使用

利用QueryCommand对象传递查询条件与使用HTML技术中的FORM的Submit技术还是有一些差别的。这种差别也是很多刚刚接触dorado技术的程序员容易犯错的一个地方。
Dorado对于Form的Submit技术作了MVC分解,需要上传的数据信息统一使用Dataset管理,而Form只是Dataset的一个展示视图,应此在QueryCommand的使用中我们会使用到三个对象:
Dataset:管理查询条件的值;
Form(通常情况下都是指AutoForm):Dataset的视图,用以维护dataset的数据;
QueryCommand;
于是参数上传的处理细节变成:
利用AutoForm维护Dataset的数据
利用QueryCommand提交Dataset的数据
图表8中的QueryCommand在开发时就应该定义为:
查询条件Dataset:dsCondition

<Dataset id="dsCondition" type="Form">
<Fields>
<Field name="employee_name" label="姓名(Like)"/>
<Field name="dept_id"/>
<Field name="dept_name" label="部门(=)" dropDown="dropdownDept"/>
<Field name="sex" label="性别(=)"/>
<Field name="married" label="婚否(=)"/>
<Field name="salary1" label="薪水(>=)" dataType="int"/>
<Field name="salary2" label="薪水(<=)" dataType="int"/>
<Field name="birthday1" label="生日(>=)" dataType="date"/>
<Field name="birthday2" label="生日(<=)" dataType="date"/>
</Fields>
</Dataset>

AutoForm:
Xml配置:

<Control id="formQuery"
type="AutoForm"
dataset="dsCondition"
width="100%">
</Control>

之后我们在JSP中添加代码:

<d:AutoForm id="formCondition" />

QueryCommand:

<Control id="cmdQuery"
type="QueryCommand"
conditionDataset="dsCondition"
queryMode="form-post">
</Control>

注意QueryCommand没有action属性,这是因为QueryCommand都是针对本页面的操作,如果要向另一个页面传递参数请参考RequestCommand,使用RequestCommand实现。
QueryCommand通过conditionDataset属性指定将具体的dataset的管理数据上传,并通过queryMode指定上传方式:form-get,form-post,flush.对于form-get,form-post都比较简单。唯一需要注意的是当中文作为参数来传递的时候开发人员最好设置传递方式为form-post并且接受中文信息的页面的编码调整为GBK才可。JSP编码设置如下:

<%@ page contentType="text/html; charset=GBK" %>

QueryCommand的原理很简单,但是在dorado的开发当中基本上不会使用form-get或者form-post实现查询(如果真有需要就使用RequestCommand).一般都是使用flush方式,这个也是QueryCommand的queryMode的默认属性。
下面看看flush的使用方式:
通常在dorado开发当中图表8的页面都没必要使用IFrame技术实现,也不需要通过刷新页面的方式实现查询。而是整个页面包含两个主要的组件,查看一下jsp的定义:

<d:View config="sample.training.data.Query">
<d:AutoForm id="formQuery" />
<d:DataTable id="tableEmployee" />
</d:View>

根据QueryCommand的定义方式需要定义一个dataset用于保存查询条件的值,范例命名为dsCondition:

<Dataset id="dsCondition" type="Form">
<Fields>
<Field name="employee_name" label="姓名(Like)"/>
<Field name="dept_id"/>
<Field name="dept_name" label="部门(=)" dropDown="dropdownDept"/>
<Field name="sex" label="性别(=)"/>
<Field name="married" label="婚否(=)"/>
<Field name="salary1" label="薪水(>=)" dataType="int"/>
<Field name="salary2" label="薪水(<=)" dataType="int"/>
<Field name="birthday1" label="生日(>=)" dataType="date"/>
<Field name="birthday2" label="生日(<=)" dataType="date"/>
</Fields>
</Dataset>

视图模型中定义formQuery的dataset属性为dsCondition:

<Control id="formQuery"
type="AutoForm"
dataset="dsCondition"
width="100%">
</Control>

 

tableEmployee主要用来展示查询结果,同样在视图模型中需要定义一个dataset:

<Dataset id="dsEmployee"
type="Reference"
sourceModule="sample.HR"
sourceDataset="dsEmployee"
readOnly="true"
pageSize="10"
showLoadingTip="true"
async="true">
<Fields>
<Field name="employee_id" dataType="string"/>
<Field name="EMPLOYEE_NAME" dataType="string"/>
<Field name="dept_id" dataType="string"/>
<Field name="sex" dataType="boolean"/>
<Field name="married" dataType="boolean"/>
<Field name="birthday" dataType="date"/>
<Field name="salary" dataType="double"/>
<Field name="dept_name" dataType="string"/>
</Fields>
</Dataset>

tableEmployee绑定到dataset上:

<Control id="tableEmployee"
type="DataTable"
dataset="dsEmployee" width="100%" height="100%">
<Column name="employee_id" field="employee_id" />
<Column name="EMPLOYEE_NAME" field="EMPLOYEE_NAME" />
<Column name="sex" field="sex" />
<Column name="married" field="married" />
<Column name="birthday" field="birthday" width="120" />
<Column name="salary" field="salary" />
<Column name="dept_name" field="dept_name" width="150" />
<Events />
</Control>

利用dataset的flushData功能,我们可以通过如下的方式实现页面无刷新的数据查询功能:

  • 利用dsEmployee的parameters存储dsCondition中用户输入查询条件

    dsEmployee.parameters().setValue("employee_name",dsCondition.getValue("employee_name"));
    dsEmployee.parameters().setValue("dept_id",dsCondition.getValue("dept_id"));
    dsEmployee.parameters().setValue("dept_name",dsCondition.getValue("dept_name"));
    dsEmployee.parameters().setValue("sex",dsCondition.getValue("sex"));
    dsEmployee.parameters().setValue("married",dsCondition.getValue("married"));
    dsEmployee.parameters().setValue("salary1",dsCondition.getValue("salary1"));
    dsEmployee.parameters().setValue("salary2",dsCondition.getValue("salary2"));
    dsEmployee.parameters().setValue("birthday1",dsCondition.getValue("birthday1"));
    dsEmployee.parameters().setValue("birthday2",dsCondition.getValue("birthday2"));

  • 调用dataset的flushData()方法将参数传递到服务器,并清空客户端缓存数据从服务器上获取新的查询结果。

    dsEmployee.flushData();

    关于flushData()方法的详细使用请参考dataset中关于flushData()的原理介绍.这儿并不是我们的重点。我们主要观察dsCondition信息存储到dsEmployee.parameters()对象中这一段代码。QueryCommand可以通过配置方式自动实现这一过程,而不需要开发人员写脚本:

    <Control id="cmdQuery"
    type="QueryCommand"
    queryDataset="dsEmployee"
    conditionDataset="dsCondition" async="true">
    </Control>

    注意queryDataset的属性配置,QueryCommand根据这个属性会自动实现dsEmployee的parameters()赋值处理,会自动的从conditionDataset中取出用户输入的查询条件存入parameters()中。这样我们只需要调用:

    cmdQuery.execute();

    该方法自动实现parameters()的赋值并调用dsEmployee的flushData()方法。

    常用技巧

    动态添加参数

    从上面的讨论中可以知道QueryCommand只负责将属性配置中conditionDataset的所有数据作为参数保存到queryDataset对象中,如果开发时需要传递ConditionDataset之外的数据怎么办呢?这个可以通过QueryCommand的parameters()集合实现,如:

    cmdQuery.parameters().setValue("用户自定义参数名","用户自定义参数值");
    cmdQuery.execute();

    cmdQuery执行时就会把自身的parameters()复制到queryDataset的parameters()中。

    模糊查询处理

    很多系统的数据处理层在处理模糊查询时,只提供like处理机制,但是并没有解决%的添加功能,这就要求业务逻辑处理层甚至是表现层代码负责生成相关的%信息交给数据处理层。例如dorado中的DBDataset的like查询功能的%就得在查询之前准备好,%的添加既可以在业务逻辑处理层上也可以在客户端实现。从逻辑严密性上来说我们依然建议您在业务逻辑处理层实现,但是相信你我也依然喜欢一种简洁的编程,是否把这种较为简单的技术处理放到客户端进行,答案当然是可以。下面我们举例说明如何在客户端实现%添加机制.
    在上述用例中,如果实现对部门名称的模糊查询,注意看QueryCommand的处理细节:

    QueryCommand.prototype.execute = function() {
    this._fireBeforeExecute();

    var conditionDataset = this.getConditionDataset();
    var queryDataset = this.getQueryDataset();

    if (queryDataset == null) return false;

    if (conditionDataset != null && conditionDataset._current != null) {
    if (conditionDataset._current._state == __Record_STATE_NEW) {
    conditionDataset._current._dirty = true;
    }
    if (!conditionDataset.postRecord()) {
    return false;
    }
    }

    if (this._queryMode == __QueryCommand_QUERYMODE_FLUSH) {
    var parameters = queryDataset._parameters;
    if (conditionDataset != null && conditionDataset._current != null) {
    var fieldCount = conditionDataset.getFieldCount();
    for (var i = 0; i < fieldCount; i++) {
    var value = conditionDataset.getValue(info);
    parameters.setValue(conditionDataset.getField(info)._name, value);
    }
    }

    var params = this._parameters;
    var paramSize = params.size();
    for (var i = 0; i < paramSize; i++){
    var param = params.getParameter(info);
    parameters.setValue(param._name, param._value);
    }

    queryDataset.flushData();
    }

    this._fireOnSuccess();
    }

    以上的代码主要分三段:
  • 复制conditionDataset的值到queryDataset的parameters()中;
  • 复制command的parameters()到queryDataset的parameters()中;
  • 调用queryDataset的flushData方法实现查询功能

根据以上的机制在1步骤完成之后queryDataset的parameters()中部门名称的信息是不包含%的,下面我们通过如下代码处理:

var deptName = dsCondition.getValue("dept_name");
if (deptName==""){
cmdQuery.parameters().setValue("dept_name","");
}
else{
cmdQuery.parameters().setValue("dept_name","%"deptName"%");
}
cmdQuery.execute();

我们在cmdQuery的parameters中保存经过%处理的dept_name信息,这样在上述的第二步处理之后queryDataset中保存的dept_name参数就是经过%处理的数据了。从而就可以交给后台的数据处理层,不需要再作任何处理就可以直接使用like查询。
在实现的技巧上我们通常建议上述%处理的代码在QueryCommand的beforeExecute()中实现,该方法是在QueryCommand执行execute()之前总能执行,从而保证不管怎么调用,都会自动实现%处理,该种处理方式也可以参考doradosample中提供的范例:
http://www.bstek.com/dorado5/training/data/query.jsp

ConditionDataset中字段名的命名规则

如果没有后台业务逻辑处理层,例如直接使用DBDataset实现默认的查询机制,就尽量使ConditionDataset中的字段名与QueryDataset查询需要使用的参数名保持一致,这样就无需在调用数据处理层时再Mapping映射转换一次。

使用FormDataset

ConditionDataset的开发时,一般建议使用Form类型的Dataset实现,由于FormDataset与HttpServletRequest密切相关性,当一个页面的查询如果与特定的HttpServletRequest有关。例如在调用这个页面的时候我们通过如下的url调用员工列表页面:

employeeList.jsp?dept_id=D11

则我们就可以利用FormDataset自动存储dept_id的信息,并让它参与每一次的数据查询工作。
关于FormDataset与HttpServletRequest的相关性请参考FormDataset的相关文档。
另外如果QueryCommand使用queryMode为form-get,form-post方式时,ConditionDataset就必须使用FormDataset,这样能够保证FormDataset可以自动地接受来自于HttpServletRepuest的信息。

异步处理

部分Command支持异步处理,如:

  • QueryCommand;
  • UpdateCommand;
  • RpcCommand;
  • MarmotUpdateCommand;
  • MarmotRpcCommand

实现方法比较简单,只要设置Command的async属性为true:

<Control id="cmdQuery"
type="QueryCommand"
queryDataset="dsEmployee"
conditionDataset="dsCondition" async="true">
</Control>

这样Command对象在执行时就会以异步的方式处理,在Command的异步处理中还需要充分利用它的onSuccess以及onFailure事件。利用这两个事件实现回调功能。
如在onSuccess中写入js代码:

alert("异步调用成功结束!");

在onFailure中写入代码:

alert("异步调用出错!");

动态编程

在服务器段动态生成QueryCommand

protected void initControls() throws Exception {
super.initControls();//系统默认函数,注意保留
QueryCommand cmdQuery= (QueryCommand)createControl("QueryCommand", " cmdQuery");
cmdQuery.setConditionDataset("dsCondition");//关联查询条件dataset
cmdQuery.setQueryDataset("dsEmployee");
cmdQuery.setQueryMode("flush");
}

使用视图模型实现类提供的createControl方法创建QueryCommand对象,注意第一个参数用以指定组件的类型,第二个参数指定新建对象的id

在服务器段获得View(视图模型)中定义的QueryCommand,并动态设置它的属性

protected void initControls() throws Exception {
super.initControls();//系统默认函数,注意保留
QueryCommand cmdQuery= (QueryCommand)getControl("cmdQuery");
cmdQuery.setConditionDataset("dsCondition");//关联查询条件dataset
cmdQuery.setQueryDataset("dsEmployee");
cmdQuery.setQueryMode("flush");
}

在上面的代码中可以看到通过实现类提供的getControl函数获得我们需要的组件对象,所需要的就是给它一个QueryCommand对象的id,注意开发时我们建议您直接在View的设计中添加一个空QueryCommand组件,这样我们就可以在initControls方法中通过getControl方法获得这个空的QueryCommand对象,并利用该对象提供的API进行各种设定和初始化的工作,如QueryCommand的setConditionDataset(), setQueryDataset()方法。

主要属性说明

基本属性参考ListDropDown的属性说明

属性

说明

async

是否支持异步查询(queryMode为flush时有效)

conditionDataset

用户输入的查询条件信息所属的dataset对象

queryDataset

执行查询动作的dataset

queryMode

查询方式
flush:利用dataset的flushData()方法执行查询
form-get:传统刷新页面的查询,参数采用get方式转递
form-post:传统刷新页面的查询,参数采用post方式传递
特别说明:如果采用的form方式查询,ConditionDataset对象的类型要改用FormDataset
该参数默认为flush方式执行查询

showLoadingTip

使用flushData方式实现查询时是否显示一个正在下载的提示框

主要事件说明

事件

说明

Public DoradoException beforeExecute(Command command)

在命令执行之前触发
如果您返回了一个异常,那么系统将显示此异常信息并终止后续默认的操作.
如果您返回null或者没有定义任何返回值,那么系统将继续执行后续默认的操作.
如果您希望终止后续默认的操作但又不希望显示任何异常信息,那么请返回一个AbortException对象.
Parameters
command - Command - 触发事件的命令对象

Public DoradoException onSuccess(Command command)

在命令执行成功时触发.
如果您返回了一个异常,那么系统将显示此异常信息并终止后续默认的操作.
如果您返回null或者没有定义任何返回值,那么系统将继续执行后续默认的操作.
如果您希望终止后续默认的操作但又不希望显示任何异常信息,那么请返回一个AbortException对象.
Parameters
command - Command - 触发事件的命令对象

Public DoradoException onFailure(Command command)

在命令执行失败时触发.
如果您返回了一个异常,那么系统将显示此异常信息并终止后续默认的操作.
如果您返回null或者没有定义任何返回值,那么系统将继续执行后续默认的操作.
如果您希望终止后续默认的操作但又不希望显示任何异常信息,那么请返回一个AbortException对象.
Parameters
command - Command - 触发事件的命令对象