这是关于最近开发的开源项目Semantika的系列文章的第二篇。如果还没有阅读第一篇文章,请先跳转到以下链接阅读,然后再回来继续阅读这篇文章。
在本文中,将介绍Semantika使用的从关系型到领域模型的映射概念及其语言特性。语言语法将采用Termal/XML格式。请注意,从Termal文档可以生成等效的R2RML文档,反之亦然。
假设读者对Java编程、关系型数据库和XML有一定的了解。了解语义网技术如OWL、RDF和SPARQL将有所帮助,但不是必需的。
领域到关系型映射的概念可以被理解为一种正式的规范,它定义了领域词汇和数据库数据模式之间的关系。它用于将数据库中的数据映射到本体中的类和属性的实例。从概念上讲,映射模型携带了一个公式,当应用这个公式时,可以为源表或SQL视图的每一行产生对象实例。
例如,假设有一个员工数据表如下:
EMP_NO | BIRTH_DATE | FIRST_NAME | LAST_NAME | GENDER | HIRE_DATE |
---|---|---|---|---|---|
10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 |
使用上述映射模型,模型能够产生如下等效的对象实例:
在Semantika中,每个映射语句被称为映射公理。这些公理将帮助回答用户查询,通过将每个查询部分转换为原生SQL查询字符串。然后查询执行会转到底层数据库引擎,并返回结果。
Semantika使用XML文档来读取映射语句。文档定义简单明了,易于手动编辑。如果熟悉R2RML规范,那么这一节只是一个简单的介绍。但如果不熟悉,这只是另一种关于文档配置的介绍。
以下是一个Termal/XML格式的映射文档示例。绿色项目符号是将有进一步评论的部分。
前缀用于简化长名称的书写形式。通常,前缀的命名空间使用URI字符串以确保其唯一性。例如,而不是写一个长资源名称:
http://obidea.com/ex/ontology/work#Employee
http://obidea.com/ex/ontology/work#lastName
可以简化为:
emp:Employee
emp:lastName
只要在文档中指定了emp
前缀有一个指向http://obidea.com/ex/ontology/work#
的URI命名空间。
注意上面的示例使用了空前缀。这是一个特殊的概念,用于定义默认前缀。这意味着如果资源名称没有分配前缀,它将被假定为使用默认前缀。
URI模板是构建对象标识符的全局定义,并且高度可重用。它由名称和模板字符串组成。模板字符串有一个特殊的语法,用于通过在花括号中包含一个整数来引用列("{"和"}")。任何映射都可以使用特定的URI模板,使用模板调用。
以下示例定义了一个名为"Employee"的URI模板和一个模板字符串,其中有两个列引用。通过subject-map进行了模板调用,它使用DEPT_NO
和EMP_NO
列作为标识符组件。
<uri-template tml:name="Employee" tml:value="http://obidea.com/ex/ontology/work?department={1}&employee={2}">
<mapping>
...
<subject-map rr:template="Employee(DEPT_NO,EMP_NO)"/>
</mapping>
应该注意到这种方法类似于编程范式中的函数调用,其中函数名称与模板名称关联,参数是列引用。
在详细讨论每个示例案例之前,让简要介绍TriplesMap中的几个重要元素。
逻辑表元素的内容表示将要映射到对象实例的数据行。有两种方式指定逻辑表:
使用rr:tableName
属性指定用于映射的SQL表。值必须是一个有效的模式限定名称,指向输入数据库中的现有表或视图。
编写一个SQL查询以选择用于映射的特定数据区域。查询字符串必须是一个有效的SELECT查询,可以在输入数据库上执行。
subject-map本质上是为逻辑表中的每一行分配一个对象标识符。映射可能有一个类IRI(即,由rr:class
属性表示)来明确声明对象类型。
谓词-对象映射用于将类属性或关系到逻辑表中的特定列进行映射。请注意,属性和关系是两个不同的概念,即,一个属性与一个类型值相关联,而一个关系与一个对象引用相关联,由对象标识符表示。
回到示例案例,第一个映射向展示了一个简单的映射模型,用于捕获员工的个人资料。
<mapping>
<logical-table rr:tableName="EMPLOYEES"/> (1)
<subject-map rr:class="Employee" rr:template="Employee(EMP_NO)"/> (2)
<predicate-object-map rr:predicate="firstName" rr:column="FIRST_NAME"/> (3)
<predicate-object-map rr:predicate="lastName" rr:column="LAST_NAME"/> (4)
<predicate-object-map rr:predicate="hireDate" rr:column="HIRE_DATE"/> (5)
</mapping>
第(1)行指定了EMPLOYEES
表用于映射数据源。
第(2)行指示Employee(EMP_NO)
用于标识表EMPLOYEES
中的行,并且所有生成的实例都属于一个类Employee
。
第(3)、(4)、(5)行指定了类Employee
的几个属性映射。
第二个映射向展示了关系映射的示例。
<mapping>
<logical-table rr:tableName="DEPT_EMP"/> (1)
<subject-map rr:template="Employee(EMP_NO)"/> (2)
<predicate-object-map rr:predicate="worksIn" rr:template="Department(DEPT_NO)"/> (3)
</mapping>
第(1)行指定了DEPT_EMP
表用于映射数据源。
第(2)行指示Employee(EMP_NO)
用于标识表DEPT_EMP
中的行。然而,生成的实例将没有类型。
第(3)行指定了一个worksIn
关系,它关联了一个由Employee(EMP_NO)
标识的对象和另一个由Department(DEPT_NO)
标识的对象。
第三个映射向展示了使用SQL查询作为逻辑表的示例。
<mapping>
<logical-table> (1)
<![CDATA[ (2)
select EMP_NO (3)
from TITLES (4)
where TITLE = 'Staff']]> (5)
</logical-table> (6)
<subject-map rr:class="Staff" rr:template="Employee(EMP_NO)"/> (7)
</mapping>
</code>
第(1)-(6)行指定了一个用于映射数据源的SQL查询。该查询返回所有职位为'Staff'的员工。
第(7)行指示Employee(EMP_NO)
用于标识SQL查询返回的行,并且所有生成的实例都属于一个类Staff
。
希望以一种简单的方式呈现这篇文章,以便所有读者都能轻松理解映射建模的基本用法。根据领域复杂性和数据模式的大小,开发映射模型可能是一个具有挑战性但最终有回报的任务。此外,映射模型的形状会影响查询性能。(也许会在另一篇文章中介绍一些技巧和窍门)。
因此,以这篇文章作为总结:
- 定制映射模型需要对领域和数据库都有良好的理解。
- 将映射模型视为公司资产。它是一种知识资产,指定了业务领域和数据之间的联系。
- 通过映射模型意味着已经学会了80%的Semantika。