技术开发 频道

服务数据对象简介

  SDO 客户机例示了 DMS,并从中获得不同雇员的数据图。得到数据图后,通过根对象(使用 SDO 动态 API)遍历和访问数据对象,如下所示:

1 // Get the SDO DataGraph from the DMS.
2 DataGraph employeeGraph = mediator.get(employeeName);
3 ...
4 // Get the root object
5 DataObject root = employeeGraph.getRootObject();
6 ...
7 // get the employee under the manager
8 employee = theManager.getDataObject("employees.0");
9

  客户机调用动态 SDO 访问器 API,从数据对象中获得信息,并将其显示到控制台上:

1 System.out.println("Name: " + employee.getString("name"));
2
3   System.out.println ("Number: " + employee.getInt("number"));
4
5   ...
6
7   System.out.println ("Is manager?: " +
8
9   (employee.getBoolean("manager") ? "yes" : "no") + "\n");
10
11

  我们已经看到客户机是如何获取信息(读)的,但是如何写入信息呢?更准确地说,客户机如何修改对象?SDO 客户机一般使用 DataObject 写访问器方法更新数据对象。比如,下面的代码修改从雇员 Terence Shorter 获得的数据图:

1 employee.setString("department", newDepartmentName);
2

  注意,客户机没有调用日志方法。DMS 负责对数据图的变更摘要调用 beginLogging() 和 endLogging() 记录日志。

  数据图

  可以将数据图的数据格式(模型)看作 DMS 与客户机之间的抽象。客户机期望从 DMS 得到数据图,DMS 也知道如何创建数据读(包括从后端数据源中读取和更新数据)。如果熟悉 XML 或者 Web 服务,那么可以将数据图模型看作定义数据对象 XML Schema(XSD)。数据图本身就类似 XML 实例文档。事实上,XML Schema 也是定义 SDO 模型的一种方法。

  注意,数据图及其模型总是能够序列化为 XML。在 SDOClient.java 中将 debug 变量设为 true ,就可以看到运行时在控制台中显示的数据图的序列化版本,与清单 3 类似。

  清单 3. 数据图的序列化版本

1 <?xml version="1.0" encoding="UTF-8"?>
2 <sdo:datagraph xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3   xmlns:company="http://com.example.company.ecore"
4   xmlns:sdo="commonj.sdo"
5   xmlns:sdo_1="http://www.eclipse.org/emf/2003/SDO">
6   <changeSummary>
7     <objectChanges key="#//@eRootObject">
8       <value xsi:type="sdo_1:EChangeSummarySetting"
9         featureName="department" dataValue="Board"/>
10     </objectChanges>
11   </changeSummary>
12   <company:Employee name="The Big Boss"
13     number="1" department="The new department" title="Mr."
14     manager="true">
15     <employees name="Terence Shorter" number="2"
16       department="The new department" title="Mr." manager="true">
17       <employees name="Miles Colvis" number="3"
18         department="The new department" title="Mr."/>
19       <employees name="John Datrane" number="4"
20         department="The new department" title="Mr."/>
21     </employees>
22   </company:Employee>
23 </sdo:datagraph>
24

    在本例中,数据图由 Employee 数据对象(以及变更摘要)组成。 Employee 包含姓名、编号、部门、职务、上司(管理该雇员的另一位雇员)和下属(该雇员管理的其他雇员)等属性。该例中,雇员已经位于硬编码的数据源中时,DMS 返回的数据图的格式总是雇员的上司(如果有的话)、雇员及其直接下属(如果有的话)。

  DMS 构建数据图

  SDO 1.0 没有规定 DMS API,API 应该包含数据图模型本身的设计和创建。因为建立对数据源的访问要考虑很多种情况,设计数据图本身可以作为另一篇文章的主题。

  对这个例子,我们将采用 DMS 通过动态 EMF API 定义的雇员模型。示例数据图没有 XSD 这样的模型文档。实际上,因为对象是动态生成的,所以这意味着没有生成 Employee Java 类。如果使用静态方法,就会生成这样的类。

  DMS 使用不同的数据访问 API(JDBC、SQL 等)从不同数据源获取的信息。但是,一旦从后端(该例中是硬编码的知识)检索到信息,DMS 就转而使用 EMF API(eSet、eGet)而非 SDO API 来创建数据对象的数据图。这种方法能得到较好的性能,但缺点是不能跨 SDO 实现移植。

  对于主要关注性能,同样的 DMS 设计也可使用 SDO API 实现。在这种情况下,DMS 类( employeeClass 、 employeeNameFeature 等)中缓冲的元对象,将代替 EMF 类型 EClass 、 EAttribute 和 EReference 等),成为 commonj.sdo.Type 和 commonj.sdo.Property 的类型。此外,如果性能不重要,那么可以方便地使用基于 String 的 SDO API(如 setBoolean(String path, boolean value) ),因此不需要缓冲元对象。不幸的是,这种解决方案虽然更方便,但运行起来也更慢。

  下面的代码片段说明了 SimpleEmployeeDataMediatorImpl.java 中 Employee 模型的定义。但这些代码并没有构建 SDO 对象,构建的仅仅是 SDO 对象的模型:

1 protected EClass employeeClass;
2 protected EAttribute employeeNameFeature;
3 protected EReference employeeEmployeesFeature;
4 ...
5 employeeClass = ecoreFactory.createEClass();
6 employeeClass.setName("Employee");
7 EAttribute employeeNameFeature = ecoreFactory.createEAttribute();
8 ...
9     
10 // employees (that the employee manages)
11 employeeEmployeesFeature = ecoreFactory.createEReference();
12 employeeEmployeesFeature.setContainment(true);
13 ...
14 EPackage employeePackage = ecoreFactory.createEPackage();
15 employeePackage.getEClassifiers().add(employeeClass);
16 ...
17

  注意,我们使用值 true 对 employees EReference 调用 setContainment ,因此每个雇员都“包含”他或她的下属。否则,嵌套的雇员就不会(即被包含)在数据图中,变更摘要也只能包含根对象的修改,而不能包含其他雇员的修改。

  建模 SDO

  您可能想“很有趣,但是这样只能得到 EMF 对象而不是 SDO 对象,还有什么窍门吗?”当然有,而且很简单。 Employee EClass 属于 employeePackage EPackage ,并且包含下面的调用:

1 // Have the factory for this package build SDO Objects
2
3   employeePackage.setEFactoryInstance(
4
5   new DynamicEDataObjectImpl.FactoryImpl());
6
7

  运行时,这个工厂将创建 DynamicEDataObjectImpl 类型的对象,该对象实现了 DataObject 接口(即它是一个 SDO 数据对象),而不是默认的 DynamicEObjectImpl 接口,并且可以智能地创建普通的 EMF 对象。SDO 和 EMF 对象之间的关系就很清楚了:SDO 对象就是同时实现了 SDO DataObject 接口的 EMF 对象。事实上,这些附加方法的实现是委派给核心 EMF 方法实现的。

  创建 SDO 实例

  现在,已经拥有了我们的数据对象,就可以建立 Employee 的实例并设置不同的属性了。如前所述,我们将使用 EMF API 来最大程度地提高性能。

1 EObject eObject = EcoreUtil.create(employeeClass);
2
3   // Note: we could cast the object to DataObject,
4
5   // but chose to use EObject APIs instead.
6
7   eObject.eSet(employeeNameFeature, name);
8
9   eObject.eSet(employeeNumberFeature, new Integer(number));
10
11   ... ...
12

  可以使用“employees”引用把雇员“链接”在一起,比如:

1 ((List)bigBoss.eGet(employeeEmployeesFeature)).add(terence);
2

  一旦创建了数据对象,还需要将其连接到数据图中。我们首先要调用数据图的 setRootObject() 方法,传递希望用作根的数据对象,这里使用 Employee The Boss 。

1 EDataGraph employeeGraph = SDOFactory.eINSTANCE.createEDataGraph();
2
3   ... ...
4
5   employeeGraph.setERootObject(rootObject);
6
7

  继续讨论数据图之前,要先开始记录日志。如果希望使用 SDO,那么在改动数据图之前,要先调用其变更摘要的 beginLogging() 方法。基本的原理就是清除以前的变化,开始监听新的修改。

1  // Call beginLogging() so that the Change Summary is
2
3   // populated when changes are applied to the Data Graph.
4
5   // The DMS should call beginLogging() and endLogging(),
6
7   // not the client.
8
9   employeeGraph.getChangeSummary().beginLogging();
10
11

  DMS 的另一项任务(按照 EmployeeDataMediator 接口的定义)是根据 SDO 客户机提供的数据图更新后端数据源。

0