摘要:了解如何利用 Oracle XML DB、Python 脚本和 PL/SQL 存储过程构建自己的受 Oracle 数据库支持的 Python 应用程序。
尽管 Python 很快在开发人员之中普及,但长久以来 Oracle 数据库一直是最出色的企业级数据库。采用有效的方式将这两者结合在一起是比较令人感兴趣的主题,但这实际上是真正的挑战,因为二者都要付出很多。
尽管受到警告,但本文并不会对最杰出的 Python 和 Oracle 数据库特性进行概述,而是提供一系列独立的示例。本文借助一个示例让您了解如何采用互补的方法尝试将这两种技术结合使用。尤其是,本文将指导您利用 PL/SQL 存储过程(在 Python 脚本中编排其调用)创建 Oracle 支持的 Python 应用程序,该应用程序在 Python 和数据库中实施业务逻辑。
正如您将在本文中学习到的,即使是轻型的 Oracle 数据库 10g 快捷版 (XE) 也可以得到有效利用,作为数据驱动的 Web 应用程序的数据库后端,其前端层使用 Python 构建。特别是,Oracle 数据库 XE 支持 Oracle XML DB,这是构建 Web 应用程序时通常需要的一组 Oracle 数据库 XML 技术。
示例应用程序
在用户使用您的应用程序时收集有关用户执行操作的信息成为一种比较流行的接收用户反馈的机制。通常,相对于让用户明确表达偏好的任何调查来说,并入在线应用程序中的点击跟踪工具可以为您提供有关用户偏好的大量信息。
举一个简单的例子,假设您想从“OTN — 新文章 RSS”页面中选取三个最新的 Oracle 技术网 (OTN) 文章标题,并将这些链接放到您的站点上。然后,您希望收集有关用户在您的站点上跟随这些链接中的每个链接的次数的信息。这就是我们的示例将要做的。现在,让我们试着弄清如何实现所有这些功能。首先,必须决定如何在应用程序层之间分发业务逻辑。实际上,决定如何在应用程序层之间分发业务逻辑可能是规划数据库驱动的应用程序最具挑战性的部分。尽管执行业务逻辑通常有多种方法,但是您的工作是找到最有效的方法。作为一般的经验,当规划数据库驱动的应用程序时,您应该认真考虑数据库中关键数据处理逻辑的实现。这种方法可以帮助您削减与在 Web 服务器和数据库之间发送数据相关的网络开销,并且可以减轻 Web 服务器的负担。
将所有这些理论应用到我们的示例上,例如,将获得插入到数据库中的文章详细信息的负担放到在数据库中创建的存储过程上,这样 Web 服务器不必再处理与维护数据完整性有关的任务。这在实践中的意义是您不必编写特定 Python 代码,这些代码负责跟踪数据库中是否存在与其链接被点击的文章有关的记录,如果不存在,则插入该记录,然后从“OTN — 新文章 RSS”页面中获取所需的所有详细信息。通过让数据库自己跟踪此类事情,您可以获得具有更高可扩展性且更不易出错的解决方案。在本例中,Python 代码将只负责从 RSS 页面获取文章链接,并在用户单击某个文章链接时向数据库发送一条消息。
图 1 给出了示例组件如何彼此交互以及如何与外部源交互的图形描述。
图 1:示例应用程序工作原理的高级视图。
本文的其余部分介绍如何实现此示例应用程序。有关如何设置和启动此示例的简要描述,可以参考示例代码根目录下的 readme.txt 文件。
准备工作环境
要构建此处讨论的示例,您需要安装以下软件组件(参见 Downloads portlet)并使其在您的系统中正常工作:
Apache HTTP Server 2.x
Oracle 数据库 10g 快捷版
Python 2.5 或更高版本
mod_python 模块
cx_Oracle 模块
有关如何安装上述组件的详细说明,可以参考另一篇 OTN 文章“为 Python Server Pages 和 Oracle 构建快速 Web 开发环境”(作者:Przemyslaw Piotrowski)。
设计基础数据库
一般来说,最好从设计基础数据库开始。假设您创建了一个用户模式并授予其创建和操作模式对象所需的所有权限,那么第一步就是创建基础表。在这种特殊情况下,您将需要一个唯一的名为 otn_articles_rss 的表,创建该表的方式如下:
CREATE TABLE otn_articles_rss (
guid VARCHAR2(100) PRIMARY KEY,
title VARCHAR2(200),
pubDate VARCHAR2(32),
link VARCHAR2(200),
clicks INTEGER
);
下一步是设计一个将在 Python 代码中调用的名为 count_clicks 的存储过程,它更新 otn_articles_rss 表中的数据。继续 count_clicks 过程之前,您必须先回答以下问题:当 count_clicks 尝试更新尚未插入到 otn_articles_rss 表中的文章记录的 clicks 字段时,会发生什么情况呢?假设一个新项目刚刚添加到 RSS 页面,然后指向该项目的链接出现在您的站点上。当有人单击该链接时,系统将从负责处理指向 OTN 文章的链接上执行的单击次数的 Python 代码中调用 count_clicks PL/SQL 过程。显然,处理第一次单击时,在 count_clicks 过程中发出的 UPDATE 语句将失败,因为现在还没有要更新的行。
要适应此类情况,您可以在 count_clicks 过程中实现一个 IF 块,如果由于 UPDATE 找不到指定的记录而将 SQL%NOTFOUND 属性设置为 TRUE 时,该块会发挥作用。在该 IF 块中,只要指定了 guid 和单击次数,您就可以先将一个新行插入到 otn_articles_rss 表中。之后,您应该提交这些更改,以便这些更改立即可用于其他用户会话,这些会话可能也需要更新新插入的文章记录的 clicks 字段。最后,您应该更新该记录,设置其 title、pubDate 和 link 字段。该逻辑可以作为一个单独的过程(比如 add_article_details)来实现,该过程的创建方式如下:
CREATE OR REPLACE PROCEDURE add_article_details (gid VARCHAR2, clks NUMBER) AS
item XMLType;
heading VARCHAR2(200);
published VARCHAR2(32);
url VARCHAR2(200);
BEGIN
SELECT extract(httpuritype.createuri(
'http://feeds.delicious.com/v2/rss/OracleTechnologyNetwork/otntecharticle').getXML(),
'//item[contains(guid, "'||gid||'")>0]')
INTO item FROM DUAL;
SELECT extractValue(item, '//title'),
extractValue(item, '//pubDate'),
extractValue(item, '//link')
INTO heading, published, url FROM DUAL;
UPDATE otn_articles_rss SET
title = heading,
pubDate = published,
link = url,
clicks = clicks + clks
WHERE guid = gid;
END;
/
正如您所见,该过程接受两个参数。gid 是其链接受到单击的文章的 guid.clks 是文章查看总次数的增量。在该过程主体中,您获得 RSS 文档的所需部分作为 XMLType 实例,然后提取信息,之后该信息将立即用于填充 otn_articles_rss 中与正在处理的 RSS 项目关联的记录。
借助 add_article_details,您可以继续下一环节,按照如下方式创建 count_clicks 过程:
CREATE OR REPLACE PROCEDURE count_clicks (gid VARCHAR2, clks NUMBER) AS
BEGIN
UPDATE otn_articles_rss SET
clicks = clicks + clks
WHERE guid = gid;
IF SQL%NOTFOUND THEN
INSERT INTO otn_articles_rss(guid, clicks) VALUES(gid, 0);
COMMIT;
add_article_details (gid, clks);
END IF;
COMMIT;
END;
/