Dorado 5 : 5.MoonDataset(定义Dataset实践) (T35)

下载

MoonDataset(自定义Dataset实践) v1.0.doc
MoonDataset工程文件.rar

目录

前言
名词
目标
扩展
初始化
两个功能
数据查询
字段集
查询动作
数据提交
字段集
提交动作
工具类
com.bstek.dorado.common.fileloader.FileLoader
com.bstek.dorado.utils.xml.XmlFactory
效果图
在Studio中添加MoonDataset
配置MoonDataset
自动生成字段
页面效果

前言

目前自定义Dataset的权威文档是Benny编写的《自定义Dataset的步骤-Benny-v10-20070903》,总结其中的步骤如下:
1. 为了在项目运行期间可以使用自定义的Dataset1. 将自定义Dataset打jar包放到项目的WEB-INF/lib下。
2. 编写view-datasets.xml放到项目的%doradohome%下。
2. 为了让studio为我们提供可视化的配置自定义Dataset功能。
1. 将自定义Dataset打jar包放到studio/lib下。
2. 编写user-view-rules.xml放到studio/config下。

名词

名称

含义

MoonDataset

本文谈论的自定义数据集。

OrigDataset

原始数据集,即被MoonDataset引用的数据集。

View配置文件

指View的XML定义文件,名字以.view.xml为后缀。

dataset节点

指View配置文件中表示dataset的XML节点。


目标

允许一个View引用在其他View中定义的Dataset,使用时与直接定义在自身View中的Dataset具有相同的效果。即:

  1. MoonDataset可以不定义字段而使用/合并OrigDataset的字段。
  2. MoonDataset可以不定义Parameters而使用OrigDataset的Parameters。
  3. MoonDataset可以不定义Properties而使用OrigDataset的Properties。
  4. MoonDataset可以不定义Event而使用OrigDataset的Event。

扩展

MoonDataset继承了com.bstek.dorado.view.data.CustomDataset。根据自身的功能目标需要定义3个属性:

名称

类型

含义

origViewModelConfig

String

OrigDataset所在的View配置文件。

origDatasetId

String

原始数据集ID,即被引用的Dataset的ID。

appendFields

boolean

合并字段?

初始化

任何类型的Dataset都有init()方法,通过继承com.bstek.dorado.view.data.DatasetSupport的该方法完成初始化的工作,API说明如下:

public void init(DoradoContext context,
XmlNode node)
throws java.lang.Exception
根据一个XML节点对象自动配置本Dataset。
Parameters:
context - DoradoContext
node - XmlNode
Throws:
java.lang.Exception

参数说明如下:

  1. XmlNode node – View配置文件中的dataset节点。
  2. DoradoContext context – 通常来说实例化dataset只需要node参数就足够了,那么context参数的作用可能是为了解决I18N问题。

功能说明如下:

1. 将node的所有attribute的值通过setXxx方法设置到dataset的属性中。
2. 如果配置了listener,则初始化监听器。
3. 根据dataset/masterlink节点初始化主从关系。
4. 根据dataset/fields/field节点初始化字段集。
5. 根据dataset/parameters/parameter节点初始化参数集。
6. 根据dataset/properties/property节点初始化属性集。

  1. 根据dataset/events/event节点初始化EventHandlers。


那么MoonDataset除了完成上面的工作外,还要初始化OrigDataset。相关方法如下:

public void init(DoradoContext context, XmlNode xmlNode) throws Exception {
super.init(context, xmlNode);
initOrigDataset();//初始化源Dataset
initSelfEventHandlers();
//将源Dataset的EventHandler合并到MoonDataset中
}

/**
* 初始化源Dataset
*
* @throws Exception
*/
protected void initOrigDataset() throws Exception {
XmlNode xmlNode = new XmlTool().getDatasetXmlNode(config
.getOrigViewModelConfig(), config.getOrigDatasetId());
ViewModel viewModel = this.getViewModel();
origDataset = (new DefaultDatasetFactory()).createDataset(viewModel,
xmlNode.getAttribute("type"), DEFAULT_MOON_PREFIX
+ this.getId());
origDataset.init(DoradoContext.getContext(), xmlNode);

Class c = this.getObjectClazz();
if (c != null) {
origDataset.setObjectClazz(c);
} else {
this.setObjectClazz(origDataset.getObjectClazz());
}
}

/**
* 将源Dataset的EventHandler合并到MoonDataset中
*/
private void initSelfEventHandlers() {
if (state == ViewModel.STATE_VIEW) {
Iterator i = origDataset.eventHandlers().entrySet().iterator();
EventHandler eventHandler = null;
String eventName = null;
Map.Entry entry = null;
while (i.hasNext()) {
entry = (Map.Entry) i.next();
eventHandler = (EventHandler) entry.getValue();
eventName = (String) entry.getKey();
if (this.getEventHandler(eventName) == null) {
this.addEventHandler(eventHandler);
}
}
}
}

说明:

1. super.init(context, xmlNode);此时MoonDataset初始化了自身的属性,并且origViewModelConfig与origDatasetId已经得到了View配置的值,但origDataset还没有初始化。
2. initOrigDataset();
初始化origDataset,依据为origViewModelConfig与origDatasetId的值。此时也调用了origDataset的init()方法。

  1. initSelfEventHandlers();初始化MoonDataset的EventHandler,合并MoonDataset与origDataset的EventHandler。


两个功能

Dataset的两大功能是:数据查询与数据提交,所以自定义Dataset的终极目标就是实现这两个功能。

数据查询

需要继承com.bstek.dorado.data.DatasetSupport类的doLoad方法,API如下:

protected abstract void doLoad(boolean createFields,
boolean loadData)
throws java.lang.Exception
根据已设定的条件读取外部数据.
Parameters:
createFields - 是否要执行自动创建字段的操作
loadData - 是否要装载数据
Throws:
java.lang.Exception

根据API的说明我们知道在这个方法体中我们要注意两点:

  1. 根据createField的布尔值控制生成dataset字段集的方式。
  2. 根据loadData的布尔值控制是否加载数据。

所以会出现下面的代码:

if(createFields){
... ...//自动生成字段
}
if(loadDada){
... ...//加载数据
}


字段集

在MoonDataset中是通过void syncFieldSet (boolean createFields)方法完成字段的同步功能的,规则如下:

  1. 如果appendFields为true,则合并MoonDataset和OrigDataset的字段集合。
  2. 否则OrigDataset以MoonDataset定义的字段集合为准。

代码如下:

/**
* 同步MoonDataset与源Dataset的字段集
*
* @param createFields
* @throws Exception
*/
private void syncFieldSet(boolean createFields) throws Exception {
if (createFields) {
this.createFieldsFromObjectClazz();
}

syncFieldSet4OrigDataset();//将源Dataset的字段合并到MoonDataset中
syncFieldSet2OrigDataset(); //将MoonDataset的字段集给origDataset
}
/**
* 将源Dataset的字段合并到MoonDataset中
*/
private void syncFieldSet4OrigDataset () {
FieldSet fs = this.fieldSet();
FieldSet ofs = origDataset.fieldSet();
Field f;
// how to appendField ?
switch (state) {
case ViewModel.STATE_DESIGN:
for (int i = 0; i < ofs.getCount(); i++) {
try {
f = ofs.getField(info);
if (!(f instanceof DummyField)) {
fs.addField(f);
}
} catch (DuplicateKeyException e) {
}
}
break;
case ViewModel.STATE_VIEW:
case ViewModel.STATE_SERVICE:
case ViewModel.STATE_REPORT:
if (config.isAppendFields()) {
for (int i = 0; i < ofs.getCount(); i++) {
try {
f = ofs.getField(info);
if (!(f instanceof DummyField)
&& !(f instanceof ViewDummyField)) {
fs.addField(f);
} else {
DummyField vdf = this.addDummyField(f.getName());
vdf.setDataType(f.getDataType());
vdf.setDefaultValue(vdf.getDefaultValue());
vdf.setProperty(f.getProperty());
vdf.setDefaultValue(f.getLabel());
vdf.setNullable(f.isNullable());
}
} catch (DuplicateKeyException e) {
}
}
}
break;
}
}
/**
* 将MoonDataset的字段集给源Dataset
*
* @throws Exception
*/
private void syncFieldSet2OrigDataset() throws Exception {
if (state != ViewModel.STATE_DESIGN) {
Field f;
FieldSet fs = this.fieldSet();
origDataset.setFieldSet(fs);
if (origDataset instanceof DatasetWrapper) {
Dataset ds = ((DatasetWrapper) origDataset).getWrappedDataset();
FieldSet ofs = ds.fieldSet();
FieldSet nfs = new FieldSet();

for (int i = 0, size = fs.getCount(); i < size; i++) {
f = fs.getField(info);
f = ofs.removeField(f.getName());
if (f != null) {
nfs.addField(f);
}
}

ds.setFieldSet(nfs);
}
}
}

其中createFieldsFromObjectClazz()的API说明如下,来自com.bstek.dorado.data.DatasetSupport类:

public void createFieldsFromObjectClazz()
自动利用反射将objectClazz中包含的属性映射成Dataset的字段.
此方法将自动为Dataset添加字段对象.
Specified by:
createFieldsFromObjectClazz in interface Dataset

合并fieldset的两大步骤:

  1. 将OrigDataset的FieldSet合并到MoonDataset的FieldSet中。
  2. 把MoonDataset的FieldSet给OrigDataset。


注:在syncFieldSet4OrigDataset()方法中,

  1. 当state为ViewModel.STATE_DESIGN时表明在处理Studio发送自动生成字段的请求,由于目前Studio不支持生成DummyField,所以此处将这种字段过滤掉了,否则会将DummyField按照Field的方式传递给Studio,这样OrigDataset的DummyField到了MoonDataset中就变成一般的字段了。
  2. 当state为ViewModel.STATE_VIEW或ViewModel.STATE_SERVICE或ViewModel.STATE_REPORT时只需要合并MoonDataset与OrigDatset的字段集。
  3. 这里没有对state为ViewModel.STATE_UPDATING情景做处理,原因是在服务器端处理UpdateCommand请求时,被提交的Dataset的字段以客户端的为准,并一同提交的服务器端。


查询动作

由于MoonDataset是利用origDataset完成查询功能,所以与此相关的依据都要传递给origDataset,包括: pageSize、pageIndex、parameters、properties。MoonDataset的查询动作的代码如下:

/**
* 加载数据的动作
*
* @param createFields
* 是否创建字段
* @param loadData
* 是否加载数据
*/
protected void doLoad(boolean createFields, boolean loadData)
throws Exception {
syncFieldSet(createFields);
if (loadData) {
int pageSize;
if ((pageSize = this.getPageSize()) >= 0) {// $$ Not caution.
origDataset.setPageSize(pageSize);
}
origDataset.setPageIndex(this.getPageIndex());

origDataset.parameters().assign(this.parameters());
origDataset.properties().assign(this.properties());

origDataset.load(loadData);
RecordSet rs = origDataset.recordSet();
this.setRecordSet(rs);

this.setPageSize(origDataset.getPageSize());
this.setPageIndex(origDataset.getPageIndex());
this.setPossibleRecordCount(origDataset.getPossibleRecordCount());
this.setPageCount(origDataset.getPageCount());

this.parameters().assign(origDataset.parameters());
this.properties().assign(origDataset.properties());
}
}

其中用到了com.bstek.dorado.utils.variant.VariantSet的assing()方法,API说明如下:

public void assign(VariantSet variants)
将本集合中的所有数据全部指派到给定的另一个集合中.
Parameters:
variants - VariantSet


数据提交

需要继承com.bstek.dorado.data.DatasetSupport的doUpdate方法,API说明如下:

protected abstract void doUpdate()
throws java.lang.Exception
把用户对Dataset中数据的操作更新到外部数据中.
Throws:
java.lang.Exception


字段集

数据提交时也会涉及到字段集的问题,所以此时syncFieldSet ()方法也会被调用,描述同数据查询。

提交动作

通常提交动作会涉及与数据库的交互,而MoonDataset则需要调用origDataset的update()方法,另外也同样涉及parameters与properties问题,MoonDataset的doUpdate方法代码如下:

protected void doUpdate() throws Exception {
/*
* @1.before update.
*/
// parameters && properties
origDataset.parameters().assign(this.parameters());
origDataset.properties().assign(this.properties());
/*
* @2.update
*/
origDataset.setRecordSet(this.recordSet());
origDataset.update();
this.setRecordSet(origDataset.recordSet());
/*
* @3.after update
*/
// parameters && properties
this.parameters().assign(origDataset.parameters());
this.properties().assign(origDataset.properties());
}

工具类

com.bstek.dorado.common.fileloader.FileLoader

文件加载器。目前Dorado中两种常见的FileLoader是:
1. com.bstek.dorado.common.fileloader.ResourceFileLoader – 针对存放在classpath中的资源加载器。
2. com.bstek.dorado.common.fileloader.PathFileLoader – 针对绝对路径资源的加载器。
注:决定Dorado使用哪种FileLoader加载资源的是决策者是com.bstek.dorado.common.fileloader.FileLoaderFactory,依据为%webroot%\WEB-INF\dorado.properties。

com.bstek.dorado.utils.xml.XmlFactory

XML解析对象的工厂类。屏蔽了解析XML使用的底层解析器,目前支持JDOM9、JDOM10、DOM4J,详情见Dorado安装目录下的support/jaxen/readme.txt。摘录如下:

dorado中自带的JDOM版本为JDOM 9(也被称为JDOM 0.9)。
如果您的部署环境中必须使用JDOM 10(也被称为JDOM 1.0), 那么请将dorado自带的jdom.jar替换为您所使用的版本,此时dorado可自动适配JDOM 9或JDOM 10。但是dorado使用的第三方工具包jaxen.jar却无法自动进行此类适配,因此您还需要手工用此处的JDOM10/jaxen.jar覆盖原先自带的同名文件。
注: dorado中默认使用的jaxen.jar(即安装目录中的/lib/thirdparty/jaxen.jar)是适用与JDOM 9的版本。

另外,dorado除可自适应JDOM 9和JDOM 10之外也可以自动适配DOM4j。如果您的希望dorado使用DOM4j作为XML解析器,那么请删除部署环境中的jdom.jar。此时dorado将自动尝试适配DOM4j。

注:某些应用服务器的运行是依赖于JDOM 的,如WebLogic、JBOSS等。在这种情况下您可能无法从部署环境中移除JDOM。

效果图

在Studio中添加MoonDataset

配置MoonDataset

自动生成字段

页面效果