内容


使用 PHP 构建语义 Web CRUD 操作

SQL 和 SPARQL 中的创建、读取、更新和删除

Comments

在开发 Web 应用程序时,为逻辑层和 UI 层创建放置服务器端代码的数据库结构是一种标准实践。要连接到数据库,服务器端代码需要执行一些基本的创建、更新、删除和 — 最重要的 — 读取记录等操作。由于 Web 应用程序的后台数据库通常都是关系数据库,因此这些 CRUD 操作都是使用众所周知的 SQL 语言执行的。但是,随着 Web 开发越来越多地采用面向对象的编程(OOP),模型也随之发生改变。

资源描述框架(Resource Description Framework,RDF)是描述对象同时保留数据含义的理想方法。简单协议和 RDF 查询语言(Simple Protocol and RDF Query Language,SPARQL — 发音为 “sparkle”)是通常用于针对该数据进行查询的语言,因为它在语句构成上匹配 RDF 本身的结构。RDF 和 SPARQL 都是所谓 语义 Web 栈(semantic Web stack)中的技术。

要彻底地应用语义 Web 理念,您可以使用 SPARQL 将传统的 Web 开发技术应用到 RDF 数据中。本文将展示如何使用简化的模式—视图—控制器(Model-View-Controller,MVC)设计模型、PHP 服务器端脚本语言和 SPARQL 连接到 RDF — 与使用关系数据库系统中的 SQL 相反。

SQL 和 SPARQL CRUD 操作

在使用 SQL 和 SPARQL 进行开发时,需要查看一下 CRUD 操作之间的异同。清单 1 显示了 read 操作的 SQL 代码。

清单 1. read 操作的 SQL 代码
SELECT realname, dob, location
FROM UserTable 
WHERE realname = "John Smith";

将这段基于 SQL 的代码与清单 2 中所示的基于 SPARQL 的代码相比较。采用这两个 read 操作的原因在于它们最易于理解、实现和说明。这对于 SQL 和 SPARQL 来说都是一样的。

清单 2. read 操作的 SPARQL 代码
PREFIX foaf:
<http://xmlns.com/foaf/0.1/> 
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?uri ?name ?dob ?location
FROM
<http://www.example.org/graph>
WHERE
{ ?uri
rdf:type foaf:Person ;
foaf:name "John Smith" ;
foaf:birthday
?dob ;
foaf:location ?location .
} ;

在比较两张清单时,您的第一个想法很可能是 SPARQL 版本明显比 SQL 版本长很多。这是事实,但是请不要误以为 SQL 必然更简单而且更干净。根据所运行引擎的不同,SPARQL 可以全部通过称为链接数据结果(linked data effect)的内容进行分发。此外,它允许拥有动态模式,因为它拥有互相链接的面向对象的透视图,与严格的 SQL 关系透视图形成对照。如果您想要把关系数据库表分隔为许多数据孤岛,则实际上使用的 SQL 代码行将比 SPARQL 多很多 — 更不必说 SQL 中会出现大量令人讨厌的 JOIN 描述符。

SPARQL 的前两行是 PREFIX 声明。根据语义 Web 理论,一切内容 — 无论是对象还是数据图来源(也是一个对象)— 都有统一资源标识符(Uniform Resource Identifier,URI)。PREFIX 行只是将临时标签应用到一些 URI 中 — 在本例中为 Friend of a Friend 和 RDF 模式。其中的好处是您以后可以在查询中使用 PREFIX 声明而不必使用完整的 URI。

SPARQL 代码的下一行描述了查询请求。这条语句在本质上与 SQL 语句相同,不同之处是对 URI 的附加请求。注意问号的使用(?)是为了表示术语是变量。

FROM 语句描述了获取数据的位置。这在 SQL 和 SPARQL 中是相同的,只是在 SPARQL 中,数据源名称是 URI,而非表示计算机或网络中某个物理位置的字符串。

两者的 WHERE 语句完全不同,因为使用 SPARQL,必须指定用于获取数据的模式。同样,如果尝试过使用关系方法执行此操作,则需要花费的代价比普通 SQL 多得多:需要使用 PHP、Java™ 编程语言或者一些其他服务器端语言才能执行数据源之间的检查。SPARQL 代码行完成的操作比较明了,这包括确保正在检索的数据只属于 Person 类型。SPARQL 将获取名称和位置,同时执行一些模式匹配以查找正确的 John Smith。

创建

SPARQL 中的 CRUD 操作通常比 read 操作更神秘。但是,可以完成这些操作。首先,create 操作将把新记录或对象插入到表或图表中。

清单 3. create 操作的 SQL 代码
INSERT 
INTO UserTable (realname, dob, location) 
VALUES ("John Smith", "1985-01-01", "Bristol, UK");

现在,比较清单 3 中基于 SQL 的代码与清单 4 中基于 SPARQL 的代码中的 create 操作。

清单 4. create 操作的 SPARQL 代码
PREFIX foaf:
<http://xmlns.com/foaf/0.1/> 
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
INSERT 
INTO GRAPH <http://www.example.com/graph> 
(?realname, ?dob, ?location) 
{
<http://www.example.org/graph/johnsmith#me> rdf:Type 
foaf:Person ;  
foaf:name "John Smith" ;  
foaf:birthday 
<1985-01-01T00:00:00> ; 
foaf:location "Bristol, UK" 
}

同样,注意 PREFIX 行与用 SPARQL 在 read 操作中执行的操作完全相同。INSERT INTO 的工作原理类似于 SQL,但是同样,这是基于 URI 的,而非基于字符串、基于表和基于名称的,这允许使用 HTTP 完成操作。您必须再次指定模式。在这里,比在 read 操作中了解起来略微简单一些,因为您几乎可以拥有任何属性,只要它与模式兼容。这就是 RDF 所提供的分布式动态可扩展对象的优点。

删除

从某一程度来说,如果要创建,那很可能需要进行删除。例如,用户可能想要删除他们在您站点中的帐户(这显然是件遗憾的事情,他们想要离开,但是他们可能有正当的理由)。清单 5 提供了典型 delete 操作的 SQL 代码。

清单 5. delete 操作的 SQL 代码
DELETE FROM UserTable 
WHERE realname = "John Smith"

现在,比较清单 5 中基于 SQL 的代码与清单 6 中基于 SPARQL 的代码。

清单 6. delete 操作的 SPARQL 代码
DELETE 
FROM GRAPH <http://www.example.com/graph>
{<http://www.example.org/graph/johnsmith#me> ?predicate ?object }

SQL 与 SPARQL 代码之间的基本差别是 SQL 删除了表中的某行,然而 SPARQL 将删除与 http://www.example.org/graph/johnsmith#me 所表示的 “John Smith” 相关的 “所有三元组”。这种差别是因为 RDF 模型是基于图表的。

更新

许多 Web 应用程序允许用户更新信息。UPDATE 操作是实现此功能的方法。清单 7 和 8 演示了如何在 SQL 和 SPARQL 中实现更新。

清单 7. update 操作的 SQL 代码
UPDATE UserTable
SET location = "Weston-super-Mare, UK"
WHERE realname = "Joanne Smith"

现在比较清单 7 中基于 SQL 的代码与清单 8 中基于 SPARQL 的代码中的 update 操作。

清单 8. update 操作的 SPARQL 代码
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
MODIFY <http://www.example.org/graph> 
DELETE {?uri foaf:location ?location} 
INSERT {?uri foaf:location "Weston-super-Mare, UK"}
WHERE { ?uri foaf:name "Joanne Smith" }

SPARQL 中的 UPDATE 可能看上去非常愚蠢,但是当您知道它并不是要更新关系表行 — 而是要更新图表中的一个非常特定的关系时,它是完全有效的。实现更新而无需达到多个位置的最简单方法是执行删除和插入。MODIFY 关键字用于建立与正确图表的连接。

连接到 SQL 和 SPARQL 数据库系统

要执行上面的 SQL 和 SPARQL 语句,必须以某种方式连接到系统中。很明显,不同的系统有不同的连接方法。一种常见方法是使用开放数据库连接(Open Database Connectivity,ODBC)驱动程序连接到一般数据库,这些驱动程序通常附带于最新版本的 Mac OS X 和 Linux® 系统中,并且可以安装到诸如 Windows® 之类的其他操作系统中。ODBC 实际上提供了一个简单的通用 API,可以连接到所选的 SQL 数据库。有趣的是,ODBC 还可以与诸如 OpenLink Virtuoso 之类的一些语义 Web 数据库结合使用。但是,大多数其他语义 Web 数据库都需要一些可以处理诸如 Seseme 和 Jena 之类系统的自定义连接过程,或者自定义通用连接系统,比如 RDF2Go(在撰写本文时,RDF2Go 仅适用于 Java 技术)。如果数据将通过 HTTP 公开,那么另一种备选方法是基于 HTTP 的 SPARQL 连接,这种方法使您的数据成为 “链接数据” 并且可以实现完全分布。由于 SQL 和 SPARQL 的连接方法的范围是不同的,因此不适合在本文中详细介绍这些方法。

通过 PHP 实现 SQL 和 SPARQL

在选择连接方法后,通常下一个步骤是在 PHP 中执行常用操作。对于自定义系统,通常需要使用 SQL 字符串,插入 PHP 变量并通过函数参数传递。该函数随后将连接到数据库中并执行此事务。此处建议对 SPARQL-RDF 连接执行与 SQL-RDBMS 连接相同的操作。

因此,看一下另一种代码比较 — 这次使用 PHP 语言 — 从 read 开始,然后是 createdeleteupdate,同上面的代码比较一样。使用名为 query_execute 的虚构的查询执行函数,该函数将获取 SQL/SPARQL 语句的字符串表示。

读取

首先是简单的 read 操作。在 PHP 中可以使用字符串连接将变量插入到查询中。

清单 9. read 操作的 SQL 代码
function readUserInfo($realname) {
    $sqlstatement = "SELECT realname, dob, location FROM UserTable WHERE
    realname = \"" + realname + "\";";
    return query_execute($sqlstatement);
    }

现在,比较清单 9 中基于 SQL 的 PHP 代码与清单 10 中基于 SPARQL 的 PHP 代码中的 read 操作。

清单 10. read 操作的 SPARQL 代码
function readUserInfo($realname) {
    $sqlstatement = "PREFIX foaf:
    <http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?uri ?name ?dob ?location 
FROM <http://www.example.org/graph>
WHERE 
{ ?uri rdf:type foaf:Person ; 
foaf:name \"" + $realname + "\" ; 
foaf:birthday ?dob ;
foaf:location ?location .
} ;";
    return query_execute($sparqlstatement);
}

正如您所见,以上函数在开发时使用函数名作为参数,这意味着您可以开始直接使用 SPARQL 版本。此外,如果使用的是 ODBC 系统,您不必担心返回类型发生变化。

创建

和上面一样。以下 PHP 方法将把变量插入到字符串中以运行动态查询。清单 11 显示了如何用 create 操作完成该动态查询。

清单 11. create 操作的 SQL 代码
function createUserInfo($realname, $dob,
    $location) {
    $sqlstatement = "INSERT INTO UserTable (realname, dob,
    location) VALUES (\"" + $realname + "\", \"" + $dob + "\", \"" +
    $location + "\");";
return query_execute($sqlstatement);
}

现在比较清单 11 中基于 SQL 的 PHP 代码与清单 12 中基于 SPARQL 的 PHP 代码中的 create 操作。

清单 12. create 操作的 SPARQL 代码
function createUserInfo($uri, $realname,
    $dob, $location) {
    $sparqlstatement = "PREFIX foaf:
    <http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
INSERT INTO GRAPH <http://www.example.com/graph>
    (?realname, ?dob, ?location) 
    { 
    " + $uri + " rdf:Type
    foaf:Person ;
    foaf:name \"" + $realname + "\" ;
    foaf:birthday
        <" + $dob + ">> ;
        foaf:location \"" + $location +
"\}"
return query_execute($sparqlstatement);
    }

为了简单起见,此函数的 SPARQL 版本将包括 URI 参数,但是,执行一些附加字符串连接使函数声明与 SQL 一致将非常简单。

删除

在 PHP 中,通过字符串连接使用插入操作来执行 delete 操作。请注意,如上一节所示,这些不同的变量传递到 SQL 和 SPARQL 版本中。清单 13 显示了 delete 操作的 SQL 代码。

清单 13. delete 操作的 SQL 代码
function deleteUserInfo($realname) {
    $sqlstatement = "DELETE FROM UserTable WHERE realname = \"" + John Smith + "\"";
        return query_execute($sqlstatement); 
}

现在,比较清单 13 中基于 SQL 的 PHP 代码与清单 14 中基于 SPARQL 的 PHP 代码中的 delete 操作。

清单 14. delete 操作的 SPARQL 代码
function deleteUserInfo($uri) {
    $sparqlstatement = "DELETE FROM GRAPH
    <http://www.example.com/graph> {<" + $uri +
    "> ?predicate ?object }"; 
    return query_execute($sparqlstatement); 
}

同样,为了简单起见,SPARQL 版本的函数将获取 URI,而非实际名称。您可以将 DELETE SPARQL 语句与 WHERE 子句结合使用以按名称而不是按 URI 查找数据,这将拥有与 SQL 版本相同的函数头。

更新

使用 PHP 在 SQL 和 SPARQL 中更新记录非常简单。不过要注意关系数据库与 RDF 提供的是不同的结构。清单 15 显示了 update 操作的 SQL 代码。

清单 15. update 操作的 SQL 代码
function updateUserInfo($realname, $location)
    {
        $sqlstatement = "UPDATE UserTable SET location = \"" + $location
+ "\" WHERE realname = \"" + $realname+ "\";";
            return query_execute($sqlstatement); 
    }

现在比较清单 15 中基于 SQL 的 PHP 代码与清单 16 中基于 SPARQL 的 PHP 代码中的 update 操作。

清单 16. update 操作的 SPARQL
function updateUserInfo($realname,
    $location) { 
    $sparqlstatement = " PREFIX foaf: <http://xmlns.com/foaf/0.1/>
MODIFY <http://www.example.org/graph>
DELETE {?uri foaf:location ?location} 
INSERT {?uri foaf:location \"" + $location + "\"}
WHERE { 
    ?uri foaf:name \"" + $realname + "\"
    }
        ";
        return query_execute($sparqlstatement);
    }

SQL 与 SPARQL 版本中的函数声明是相同的。因此,从 SQL 转换到 SPARQL 十分简单。

虽然从使用 SQL 的数据源转移到使用 SPARQL 的数据源非常简单,但是有两个重要部分需要注意:抽象级别和 SQL 与 SPARQL 的异同。您应当避免产生这两种语言实际上相同的这种错误的想法。因此最好在了解一种语言的限制的同时,研究该语言的功能 — 特别是,不同语言提供的各种语法优点和图形表示。

抽象级别

本文中的示例代码与数据库结构紧密耦合。在理论上,在必须执行表结构重组之前,SQL 和 RDBMS 只能处理某种程度的松散耦合。然而,SPARQL 和 RDF 不会出现这个问题。使用 SPARQL 与数据进行耦合可能会非常抽象,因为 RDF 是分布式但又互相链接的。本文示例的可重用性可以通过提高抽象性来改进。但是,出于我们的目的,使用了简单的紧密耦合函数来说明 SPARQL 与 SQL 的异同。

正如您在示例代码中看到的,SQL 与 SPARQL 有许多相似性。不同之处在于 RDF 具有基于 Web 的、图形化和面向对象的特性以及如何将这些特性过滤到 SPARQL 语言中。作为一项简化的通用规则,您可以将 RDF 和 SPARQL 中的三元组结构作为基本表示,依次为行(主题)的惟一主键、属性/列名(谓词或关系)及基于行和列(对象)的单元格数据。此外,SPARQL 可以充分利用 HTTP 通信,并且因此使数据可以(但不是必须)基于内部网、外部网和更广阔的 Internet 进行分发。

为什么从 SQL 迁移到 SPARQL?

从 SQL 迁移到 SPARQL 有许多原因。详细原因超出了本文的讨论范围,但是您可以受到下面的启发:

  • 需要一种分布更广泛的数据解决方案。
  • 需要在 Web 上公开数据以供人们使用和链接。
  • 您会发现 Node-Arc-Node 关系(三元组)比关系数据库模型更易于理解。
  • 可能需要以纯面向对象的角度理解数据以结合使用 OOP 范例(PHP V5 及更高版本支持 OOP)。
  • 需要构建可以在 Web 中连接到数据源的一般代理。

当然, 希望从 SQL 迁移到 SPARQL 也有许多理由,并且这些理由可能非常充分。SPARQL 是另一种查询方法,不一定要立即替换 SQL。这同样适用于关系数据和基于语义 Web 的数据。并不是说要替换,相反,最好结合使用新旧技术,从而生成一种混合系统,它可以处理旧的遗留系统、现行系统和未来系统,同时也可以被这些系统处理。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=358773
ArticleTitle=使用 PHP 构建语义 Web CRUD 操作
publish-date=12152008