级别: 中级 Wei Liu, 软件工程师, IBM
2008 年 11 月 13 日 IBM® Rational® Data Architect(RDA)是帮助企业全面理解企业信息架构的综合性工具,它目前正在迅猛发展。随着越来越多的人使用 RDA,一些客户强烈要求扩展 RDA,以满足他们独特的数据建模和集成需求。这个包含两个部分的系列向您展示如何扩展 RDA 的模型、属性视图、模型报告和验证规则。在第 1 部分,学习如何编程式地遍历和修改 RDA 模型,以及如何添加和显示定制的属性。
简介
IBM Rational Data Architect(RDA)是用于数据建模和集成的综合性开发环境。RDA 使用户可以发现各种分布式数据资产,并对它们进行建模、可视化和关联处理。RDA 是 IBM Data Studio 家族的一员,它与使用 Eclipse 构建的 Rational Software Development Platform 产品紧密集成。RDA 支持逻辑、物理、词汇表、存储、域和集成数据建模。由于越来越多的企业客户使用 RDA,因此需要扩展 RDA,以满足客户独特的数据建模和集成需求。
如前所述,RDA 是基于 Eclipse 的,因此具有很好的扩展性。在本系列中,了解更多关于扩展 RDA 所需的扩展点、API 和 factory 的知识。看看如何扩展 RDA,以便:
- 编程式地遍历和修改 RDA 模型(第 1 部分)
- 添加新的属性,并在 Properties 视图中显示它们(第 1 部分)
- 生成定制的模型报告(第 2 部分)
- 添加模型验证规则(第 2 部分)
本文假设您拥有 Eclipse 插件开发经验,以及关于 Eclipse EMF 和 BIRT 项目的基础知识。本文提供的示例代码已在 RDA V7 FP5 发行版和 Eclipse 3.2.2 上测试过。
编程式地遍历和修改 RDA 逻辑数据模型和物理数据模型
如果需要扩展 RDA,那么您需要理解它的模型结构,以遍历和修改它的模型对象。本节展示如何使用一个物理模型及其对象来实现这个目标 —— 对于逻辑模型也可以使用类似的方法。
SQL 模型作为 RDA 模型的元模型
SQL 模型是 Eclipse Model Base Project 的一部分,它是 Data Tools Platform(DTP)的基础。SQL 模型构建在 Eclipse Modeling Framework(EMF)上,它使用了业界最佳实践(比如使用 UML 开发模型驱动),并遵从最新的 SQL 标准,同时还支持 JDBC 和其他连接标准。(关于 DTP 和 EMP 的更多信息,请参阅 参考资料)。RDA 使用 SQL 模型,并将 SQL 模型扩展为它的物理数据模型的元模型。
编程式地遍历一个模型
首先创建一个物理数据模型,模型中包括一个 SAMPLE 数据库、一个 SAMP 模式和一些表和列。然后,学习如何编写 Java™ 代码来遍历 SAMPLE 数据库和列出所有的模式、表和列对象。
根据以下步骤,创建一个 SAMPLE 数据模型:
- 启动 RDA,打开 Data Perspective(如果尚未打开的话)。
- 使用 File > New > Data Design Project 创建一个数据设计项目,并输入 Data Design 作为项目名。
- 在 Database Explorer 中展开 Derby Sample Connection,找到 SAMP 模式。
- 将该 SAMP 模式从 Database Explorer 中拖放到 Data Design 项目中。
应该可以看到如图 1 所示的 SAMPLE 物理模型。
图 1. SAMPLE 物理模型
至此,您已创建好 SAMPLE 物理模型。由于 DTP SQL Model 是 RDA 元模型的基础,因此有必要看看 SQL 模型,以理解 RDA 物理模型的结构。图 2 显示了 SQL 模型的模式结构。
图 2. SQL 模型模式图
从这个图中可以看出,数据库和模式是从 SQL 对象派生而来的,因此具有属性,例如名称、描述和标签。实际上,几乎 SQL 模型中描述的所有对象都是从 SQL 对象派生出来的,它们一开始便带有名称、描述和标签属性。每个数据库对象有其专门的属性,例如字符串类型的供应商和版本。每个数据库对象有 0 到多个(0..n)模式,每个模式可以有 0 到多个(0..n)表、索引、用户定义类型和例程等。通过分别调用 schema.getTables()、schema.getIndices() 和 getUserDefinedTypes(),可以获得一个模式的所有表、索引和用户定义类型。清单 1 显示了用于读取一个模式并获取它的表的示例代码。
清单 1. 遍历一个数据库和模式并获取它们的属性的示例代码
private void TraverseDatabase(Database db) {
System.out.println("Database: " + db.getName());
System.out.println("-- Vendor: " + db.getVendor());
System.out.println("-- Version: "+ db.getVersion());
// get schemas
Iterator schemaItor = db.getSchemas().iterator();
while(schemaItor.hasNext()) {
Schema aSchema = (Schema)schemaItor.next();
TraverseSchema(aSchema);
}
}
private void TraverseSchema(Schema schema) {
System.out.println("Schema: " + schema.getName());
// get tables
Iterator tablesItor = schema.getTables().iterator();
while(tablesItor.hasNext()) {
Table aTable = (Table)tablesItor.next();
TraverseTable(aTable);
}
}
|
图 3 显示了 SQL Model 表对象,其中 Table 是一个抽象类,它可以有 1 到多个列,或者 0 到多个用户定义类型。视图是一个具有额外属性的 DerivedTable,比如查询表达式和检查类型。持久表或临时表是从 BaseTable 派生出来的。
图 3. SQL 模型表图
SQL Model 的列结构如图 4 所示。列是从 TypedElement 派生出来的,它具有一些属性,例如 nullable 和 defaultValue。一个列可以选择拥有 identitySpecifier 或 generateExpression 属性,但不能同时拥有。
图 4. SQL 模型列图
清单 2 显示了从一个表中获取列并读取它们的属性的示例代码。
清单 2. 从一个表中获取列并读取它们的属性的示例代码
private void TraverseTable(Table table) {
System.out.println("Table: " + table.getName());
// get columns
Iterator columnsItor = table.getColumns().iterator();
while(columnsItor.hasNext()) {
Column aColumn = (Column)columnsItor.next();
TraverseColumn(aColumn);
}
}
private void TraverseColumn(Column col) {
System.out.println(" Column: " + col.getName());
// get column properties
DataType dataType = col.getDataType();
System.out.println(" -- Data type: " + dataType.getName());
boolean isNullable = col.isNullable();
System.out.println(" -- Is nullable: " + Boolean.toString(isNullable));
String defaultValue = col.getDefaultValue();
System.out.println(" -- Default value: " + defaultValue);
}
|
编程式地修改模型对象
现在,您知道了如何遍历模型。接下来,可以编写代码来修改模型。例如,若要为 EMPLOYEE 表添加一个 SNN 列,可以修改 TraverseTable() 方法,并调用 AddSSNColumn() 方法,如清单 3 所示。
清单 3. 将一个 SSN 列添加到表中的示例代码
private void AddSSNColumn(Table table) {
// get database from table
Database db = table.getSchema().getDatabase();
// get the database specific model element factory
DatabaseDefinition dbDef = DataToolsPlugin.getDefault()
.getDatabaseDefinitionRegistry().getDefinition(
db.getVendor(), db.getVersion());
DataModelElementFactory factory = dbDef.getDataModelElementFactory();
Column col = (Column) factory.create(SQLTablesPackage.eINSTANCE
.getColumn());
col.setName("SSN");
CharacterStringDataType cType = (CharacterStringDataType) dbDef
.getPredefinedDataType("CHARACTER");
cType.setLength(9);
col.setDataType(cType);
EStructuralFeature feature = table.eClass().getEStructuralFeature(
SQLTablesPackage.TABLE__COLUMNS);
// using RDA CommandFactory to create a add command
ICommand cmd = CommandFactory.INSTANCE.createAddCommand(
"Add SSN column", table, feature, col);
// execute the command using RDA command manager
DataToolsPlugin.getDefault().getCommandManager().execute(cmd);
}
|
AddSSColumn 方法中的代码通过传递数据库供应商和版本的字符串,获取注册的数据库定义。然后,使用前面创建的注册的数据库定义获得特定于数据库的模型元素工厂。最后,该方法使用模型元素工厂创建一个列。它使用 RDA CommandFactory 创建一个命令。createAddCommand 方法使用指定的特性将值对象添加到所有者对象。createAddCommand 方法的 API 引用是:
public IDataToolsCommand createAddCommand(String label, EObject owner, EStructuralFeature feature, Object value)
返回的命令扩展了 EMF AbstractTransactionalCommand 类,具有重做(redo)和撤销(undo)功能。当执行 AddSSNColumn 方法时,一个 SSN 列被添加到 EMPLOYEE 表中,如图 5 所示。
图 5. 将 SSN 列添加到 EMPLOYEE 表中
添加定制的属性并在 Properties 视图中显示它们
Properties 视图是 Eclipse 工作平台提供的诸多视图中的一个(关于 Eclipse Properties 视图的更多信息,请参阅 参考资料)。通过该视图可以显示和/或编辑一个选定对象的属性。Eclipse 提供了一些扩展,用于为 Properties 视图定义定制的用户界面。可以使用扩展创建选项卡式的属性视图,如图 6 所示。
图 6. Properties 视图中的 General 选项卡
RDA 扩展了选项卡式的 Properties 视图,以显示模型对象的数据。如图 图 6 所示,当在 Database Explorer 中选择 SAMPLE 物理数据模型中的一个 EMPLOYEE 表时,可以看到 General、Columns、Relationships、Documentation 和 Annotation 选项卡。在每个选项卡中,可以看到一个或多个栏。例如,在 general 选项卡中,有用于 Name、Label 和 Schema 的栏;在 columns 选项中,有列出 Name、Primary Key、Domain、Data Type 和其他属性的栏。图 7 显示了这些不同的栏。
图 7. Properties 视图中的 Columns 选项卡
RDA 为物理、逻辑、词汇表和其他模型对象提供了很多内建的属性选项卡和栏。当编辑这些属性时,更改被持久到模型中。
定制属性选项卡和栏
由于 RDA 有很多不同的客户,因此客户很可能对与某种类型的对象相关联的属性有独特的需求。例如,为了遵从个人数据保密政策,公司可能要求将雇员的社会保险号(SSN)标记为私有。他们可能要求将雇员的薪水和奖金标记为敏感数据,只有某些职员可以查看。您可以创建自己的选项卡和栏,以便为此提供便利。在接下来的小节中,学习如何为列对象添加新的选项卡和栏,如图 8 所示。
图 8. 为列定制的 privacy 选项卡,其中包含 private data 和 masking method 栏
选项卡式属性视图使用了 3 个扩展点。每个选项卡式 Properties 视图由一个属性提供器(contributor)组成,它提供一个或多个属性选项卡。每个属性选项卡由一个或多个栏组成。一个栏是一个 GUI 组件或一个复合体(包含一组映射到一个或多个属性的组件)。
属性提供器
org.eclipse.ui.views.properties.tabbed.PropertyContributor 扩展点用于为选项卡和栏定义惟一的提供器标识符。这个标识符通常与为选项卡式属性视图提供属性的惟一工作平台部件 id 相一致。工作平台部件通过实现 ITabbedPropertySheetPageContributor 界面标识一个单独的属性提供器。
如清单 4 所示,这个扩展点将 “com.ibm.datatools.properties” 定义为 RDA 属性选项卡和栏的提供器的 ID,它可在 RDA Data Project Explorer、Database Explorer 和 Diagrams 之间共享。这个扩展点还用于定义类型映射器、标签提供程序和属性类别。RDA 用户如果使用 RDA Data Project Explorer、Database Explorer 和 Diagrams,那么就不需要用到这个扩展点。
清单 4. RDA 属性提供器
<extension point="org.eclipse.ui.views.properties.tabbed.propertyContributor">
<propertyContributor
contributorId="com.ibm.datatools.properties"
labelProvider="com.ibm.datatools.core.ui.properties.PropertyLabelProvider"
typeMapper="com.ibm.datatools.core.ui.properties.PropertyTypeMapper">
<propertyCategory category="Core"/>
<propertyCategory category="Extended"/>
<propertyCategory category="Other"/>
<propertyCategory category="UserDefined"/>
<propertyCategory category="Appearance"/>
<propertyCategory category="rulerGrid"/>
<propertyCategory category="Advanced"/>
</propertyContributor>
</extension>
|
属性选项卡
org.eclipse.ui.views.properties.tabbed.PropertyTabs 扩展点描述用于提供器的选项卡。每个选项卡归属于一个提供器(由惟一提供器标识符标识)。PropertyTabs 扩展点可以通过 PropertyTab 属性定义一个或多个选项卡。ProperyTab 定义以下属性:
-
id —— 选项卡的惟一 id
-
label —— 显示选项卡上的标签
-
category —— PropertyContributor 扩展点中用于将选项卡分组的类别
-
afterTab —— 本选项卡之前的选项卡 id
-
image —— 在选项卡上显示的可选图像
-
indented —— 布尔值,表明选项卡是否缩进
清单 5 中的 plugin.xml 代码使用 图 8 所示的扩展点添加一个 Privacy 选项卡。
清单 5. 使用 PropertyTabs 扩展点添加一个 Privacy 选项卡
<extension point="org.eclipse.ui.views.properties.tabbed.propertyTabs">
<propertyTabs contributorId="com.ibm.datatools.properties">
<propertyTab
label="Privacy"
category="Extended"
id="com.ibm.custome.PrivacyPropertiesTab">
</propertyTab>
</propertyTabs>
</extension>
|
属性栏
org.eclipse.ui.views.properties.tabbed.PropertySections 扩展点描述提供器的栏。每个栏属于由惟一提供器标识符标识的一个配置。PropertySections 扩展点可以通过 PropertySection 属性定义一个或多个栏。每个栏属于由惟一选项卡标识符标识的一个选项卡。
使用该扩展点定义的属性有:
-
id —— 栏的惟一 id
-
tab —— 该栏所在选项卡的 id
-
class —— 实现栏的类
-
afterSection —— 该栏的前一个栏的 id
-
filter —— 实现栏过滤器的类
-
enablesFor —— 一个值,表明启用该栏必须满足的选择计数
-
type —— 选择的类或接口,支持在选项卡的该栏上的显示内容
清单 6 中的 plugin.xml 代码将一个属性栏添加到 Privacy 选项卡。
清单 6. 添加属性栏的示例代码
<extension point="org.eclipse.ui.views.properties.tabbed.propertySections">
<propertySections contributorId="com.ibm.datatools.properties">
<propertySection
tab="com.ibm.custome.PrivacyPropertiesTab"
class="com.ibm.datratools.extendProperties.column.SamplePropertySection"
id="com.ibm.custome.PrivacyPropertiesTab">
<!-- enable for Columns -->
<input type="org.eclipse.wst.rdb.internal.models.sql.tables.Column"/>
</propertySection>
</propertySections>
</extension>
|
属性栏的实现类
正如 清单 6 中的定义,SamplePropertySection 是实现属性栏的类。这个类应该扩展自
org.eclipse.ui.views.properties.tabbed.AbstractPropertySection 类。这个实现类的基本功能包括(参见清单 7):
- 创建用于显示 privacy 属性的 GUI 组件,即一个 privacy 复选框和一个屏蔽文本控件(createControls method)
- 从模型中读取并显示属性值(setInput 和 refresh 方法)
- 当属性值被用户更改时,保存属性值(onCheckboxSelected 和 onLeaveText 方法)
- 以 EAnnotations 的形式将属性持久化到模型中(getMaskingProperty 和 setMaskingProperty 方法)
清单 7. 实现属性栏的示例代码
public void createControls(Composite parent, TabbedPropertySheetPage page) {
super.createControls(parent, page);
TabbedPropertySheetWidgetFactory factory = getWidgetFactory();
Composite comp = factory.createFlatFormComposite(parent);
// Privacy checkbox
privacyCheckbox = factory.createButton(comp, "Private Data", SWT.CHECK);
FormData data = new FormData();
data.left = new FormAttachment(0, 0);
data.top = new FormAttachment(0, ITabbedPropertyConstants.VSPACE);
privacyCheckbox.setLayoutData(data);
privacyCheckbox.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent evt) {
Sample1PropertySection.this.onCheckboxSelected();
}
public void widgetDefaultSelected(SelectionEvent evt) {
}
});
// Masking text
CLabel label = factory.createCLabel(comp, "Masking method:");
data = new FormData();
data.left = new FormAttachment(0, 0);
data.top = new FormAttachment(privacyCheckbox,
ITabbedPropertyConstants.HSPACE);
label.setLayoutData(data);
maskingText = factory.createText(comp, "");
data = new FormData();
data.left = new FormAttachment(label, ITabbedPropertyConstants.HSPACE);
data.right = new FormAttachment(100, 0);
data.top = new FormAttachment(label, 0, SWT.CENTER);
maskingText.setLayoutData(data);
textListener = new TextChangeListener() {
protected void changeProperty(Event e) {
Sample1PropertySection.this.onLeaveText(); }
};
maskingText.addListener(SWT.FocusOut, textListener);
maskingText.addListener(SWT.DefaultSelection, textListener);
}
public void setInput(IWorkbenchPart part, ISelection selection) {
super.setInput(part, selection);
if(!(selection instanceof IStructuredSelection))
return;
Object firstObject = ((IStructuredSelection) selection).getFirstElement();
if(firstObject instanceof Column) {
column = (Column)firstObject;
refresh();
}
else {
column = null;
}
}
public void refresh()
{
super.refresh();
// read the property value
if(column != null) {
privacyCheckbox.setSelection(getPrivacyProperty());
maskingText.setText(getMaskingProperty());
}
}
private void onCheckboxSelected() {
// save the property value
boolean checkboxSelected = privacyCheckbox.getSelection();
boolean propertyStatus = this.getPrivacyProperty();
if(checkboxSelected != propertyStatus) {
setPrivacyProperty(checkboxSelected);
}
}
private void onLeaveText() {
// save the property value
String newText = this.maskingText.getText();
String oldText = this.getMaskingProperty();
if(!newText.equals(oldText)) {
setMaskingProperty(newText);
}
}
private String getMaskingProperty() {
String maskingStr = "";
EAnnotation ea = column.getEAnnotation(SAMPLE_EANNOTAITN_NAME);
if(ea != null) {
// get the details with the key SAMPLE_MASKING_PROPERTY_NAME
Object valObj = ea.getDetails().get(SAMPLE_MASKING_PROPERTY_NAME);
if(valObj != null) {
maskingStr = (String)valObj;
if(maskingStr != null)
return maskingStr;
}
}
return "";
}
private void setMaskingProperty(String maskingStr) {
EAnnotation ea = column.getEAnnotation(SAMPLE_EANNOTAITN_NAME);
DataToolsCompositeCommand cmd =
new DataToolsCompositeCommand(COMMAND_LABEL);
String newText = this.maskingText.getText();
if(ea == null) {
ea = createSampleEAnnotaiton(cmd);
}
EMap details = ea.getDetails();
int entryCount = details.size();
EStringToStringMapEntryImpl entry = null;
for(int i=0; i < entryCount; i++) {
EStringToStringMapEntryImpl anEntry =
(EStringToStringMapEntryImpl)ea.getDetails().get(i);
if(anEntry.getKey().equals(SAMPLE_MASKING_PROPERTY_NAME)) {
entry = anEntry;
break;
}
}
if(entry != null) {
EStructuralFeature feature =
EcorePackage.eINSTANCE.getEStringToStringMapEntry_Value();
cmd.compose(
new SetCommand(COMMAND_LABEL, entry, feature, newText));
DataToolsPlugin.getDefault().getCommandManager().execute(cmd);
}
}
|

 |

|
结束语
RDA 是一款综合性建模和集成工具,具有很好的扩展性。本系列包含两个部分,在第 1 部分中,您学习了如何通过扩展 RDA 编写一些代码,以便编程式地遍历和修改 RDA 模型,以及为模型对象在 Properties 视图中创建定制的选项卡和栏。
请继续关注本系列的第 2 部分,学习如何使用 BIRT 生成定制的模型报告,以及添加验证约束来强制实施业务规则。
致谢
感谢 Der Ping Chou、Anson Kokkat、Hong-lee Yu 和 Kathryn Zeidenstein 为审校本文付诸的努力。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 包含示例代码的 Eclipse 插件项目 | extendrda.zip | 9KB | HTTP |
|---|
参考资料 学习
获得产品和技术
- 下载 IBM 产品评估版
并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
关于作者  | |  | Wei Liu 是华盛顿西雅图 IBM Seattle office 的一名软件工程师。她从事数据工具和数据建模方面的工作。 |
对本文的评价
|