在 Perl 中,似乎每个人都有自己偏爱的持久性机制。这是因人而异的 — 每个人的需求都略有不同,所以每个人需要的东西也略有不同。Tangram、Alzabo、SPOPS 及其它工具都是针对 Perl 中持久性问题的有效又有用的解决方案,而它们都有一个共同点 — 在某处存储某种映射或模式。
这些映射只是将数据库中的正确位置与您请求的对象的字段连接起来。这样您就可以从数据库重新创建对象,或者将对象从内存放入数据库。我的一个朋友曾说过:“模式和模板一样,只是有些落后”。
使需求固定不变不会妨碍您 — 数据库模式会照看类,您无需担心。但是,如果您的需求要改变(谁的不会变呢?),那么您就面临着这样的问题:映射和类都必须更改。此外,我并不倾向于按照 RDMS 来考虑问题,而倾向于按照对象来考虑问题。
Pixie 提供了略微不同的方法,它不要求从类到数据库的模式或映射 — 它只存储对象。
我打算要演示的内容始终围绕着代办(todo)项。由于这个原因,也为了展示 Pixie 的能力,我随便编写了一个 todo 应用程序。首先编写的是
Todo 类:
清单 1. Todo 类
package Todo;
use strict;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
}
sub text {
my $self = shift;
my $text = shift;
if (defined( $text )) {
$self->{ text } = $text;
return $self;
} else {
return $self->{ text };
}
}
sub completed {
my $self = shift;
my $comp = shift;
if (defined( $comp )) {
$self->{ completed } = scalar(localtime(time()));
return $self;
} else {
return $self->{ completed };
}
}
|
这是非常基本的 Perl 类,它允许我做三件事:实例化新对象(
new() 方法),获取/设置该对象中文本属性的值,以及将该 todo 项标记为在某个日期/时间完成。
使用它相当简单:
清单 2. 使用 Todo
use Todo; my $todo = Todo->new(); $todo->text( "Finish writing this article" ); |
我的
Todo 类还不允许我保存那个 todo 项,如果允许的话我就可以再次看到它。
但是,这不成问题,因为只要使用 Pixie 将以上代码更改为如下所示的代码,就能实现保存功能:
清单 3. 添加 Pixie
use Todo;
use Pixie;
my $pixie = Pixie->new()->connect('bdb:todos.bdb');
my $todo = Todo->new();
$todo->text( "Finish writing this article" );
my $cookie = $pixie->insert( $todo );
|
这里出现了多行代码,因此我将试着一行行地描述它。首先,我告诉 Perl 我希望使用 Pixie 模块,然后我将 Pixie 连接到数据库。Pixie 可以连接许多不同的数据库,但现在我打算使用 BerkeleyDB 4.0,因为设置它的工作量最小。最后的更改是将 Todo 对象插入数据库中,正如我在最后一行所做的:将插入消息与我希望作为参数插入的对象一起发送到 Pixie 实例。
当我调用
insert 时,获得一个返回的字符串,我们称其为“cookie”。这个字符串唯一地标识我请求 Pixie 插入的对象,而且我可以用它在任何时候通过以下代码取回对象:
my $todo = $pixie->get( $cookie );
当然,我们不会仅仅想要存储一个 todo 项;我们需要许多项,而要记住那些 cookie 简直就是一场噩梦。为了解决这个问题,我首先要编写的是一个集合类:
清单 4. Todo 集合
package TodoCollection;
use strict;
sub new {
my $class = shift;
my $self = [];
bless $self, $class;
}
sub add_todo {
my $self = shift;
push @$self, grep { defined $_ and $_->isa('Todo') } @_;
}
sub delete_todo {
my $self = shift;
my $index = shift;
splice(@$self,$index, 1)
}
sub todo_at_index {
my $self = shift;
return $self->[$_[0]];
}
sub todolist {
my $self = shift;
return $self;
}
|
这是一个相当简单的类,它允许我在另一个名为
TodoCollection 的对象中维护 todo 列表。我可以实例化这个集合:
my $todolist = TodoCollection->new();
添加 todo 对象:
$todolist->add_todo( Todo->new()->text('write developerworks article') );
取回 todo 项:
$todolist->todo_at_index( 0 );
以及删除 todo:
$todolist->delete_todo( 0 );
我还可以获取所有 todo 项的列表:
foreach my $todo (@{$todolist->todolist}) {
print $todo->text, "\n";
}
我知道我可以存储 todo 列表并接收一个 cookie,这个 cookie 将让我取回
TodoCollection 对象,但 cookie 记起来还是很麻烦,而且我想要的是对
我更有用的东西,而不是对计算机有用的东西。Pixie 能让我做到这一点,这是它提供给用户的基本服务之一:
$pixie->bind_name( 'Todo list' => $todolist );
在这行代码中,我已经把对象的名称(Todo list)告诉了 Pixie,并请求将该名称也存储起来。Pixie 不仅会记住 todo 列表对象,还会记住其中包含的所有对象。
要取回对象,我只要请求 Pixie:
my $todolist = $pixie->get_object_named( 'Todo list' );
Pixie 记得名称
Todo list 和 TodoCollection 对象是关联的,因此将该对象交还给我。但是,Pixie 并不是以完全相同的状态把 TodoCollection 交还给我。通常,当我们获取保存其它对象的对象时,我们并不关心其中的全部对象。例如,我可能只关心我在 Todo 列表中所拥有的项的数量,而不关心实际的 todo 项本身。如果没有特殊原因的话,装入所有的存储对象是一种开销。Pixie 又可派上用场,它创建代理对象来代替通常位于集合中的 todo 对象。
代理对象无需程序员交互即可工作。当您调用其中一个 Pixie 代理对象的方法时,它神奇地连接到 pixie 数据存储并为您获取真正需要的对象。
有时您需要存储 Pixie 通常不能存储的东西。例如,当 Perl 与 C 库连接时,常用的窍门是将指向 C 结构的指针存储在标量引用中,Perl 可将该标量引用当作它的对象来使用,而 XS 粘接代码(glue code)可用该标量引用取回真正的 C 结构。这是编写粘接代码的极佳机制,但如果您希望将这类对象存储在 Pixie 中,它会引起问题,因为当 Pixie 在另一个进程中将对象返回给 Perl 时,我们要有重新创建链接的方法,这一点很重要。因为使用经过 bless 的标量引用最常见的情形就是执行粘接魔术(glue magic),所以 Pixie 在缺省情况下会认为这些对象是不可存储的。但是,情况并非总是如此;Pixie 用它的 Complicity hook 提供更改其缺省行为所需的全部机制。
为了演示这一点,我将使用来自 Pixie 文档的示例:
Set::Object 。
Set::Object 类实现了较好且快速的一个集。但 Pixie 不能直接存储这个类,因为它是基于某些 C 代码的类。首先,我们让 Pixie 知道它可以存储
Set::Object 。
Pixie 在尝试存储它希望存储的对象之前,会调用该对象的方法
px_is_storable 。我们需要改变这一点,因此我们覆盖
Set::Object 名称空间中的缺省
px_is_storable 方法:
sub Set::Object::px_is_storable { 1 }
Pixie 现在就知道您的类是可存储的。但是,它实际上还不能存储该类,因为它不能获取数据。为此,我们可以覆盖方法
px_freeze (它在您的数据被存储之前对其进行“设置”)和方法
px_thaw (在提取数据时将调用它)。
Set::Object 提供
members 方法,该方法返回由该集合的成员组成的 Perl 数组。我们将利用这一点以便能够以有意义的表示来存储我们的类:
清单 5. 存储类
sub Set::Object::px_freeze {
my $self = shift;
return bless [ $self->members ], 'Memento::Set::Object';
}
|
我们创建类
Memento::Set::Object 的一个新对象,它刚好有足够的信息供我们用于重新创建原始对象。我们通过覆盖
Memento::Set::Object 类中的
px_thaw 方法来实现这一点:
清单 6. 创建类的新对象
sub Memento::Set::Object::px_thaw {
my $self = shift;
return Set::Object->new( @$self );
}
|
当 Pixie 从数据库访存
Memento::Set::Object 类的对象时,它将调用该对象的
px_thaw 方法。当这个操作发生时,我们新的
px_thaw 方法将创建
Set::Object 实例并把 Memento 对象的内容放入其中。这将产生重新创建原始
Set::Object 实例的效果,而且在用户看来仿佛根本没有发生魔术。
有其它方法可以帮助 Pixie 存储或访存对象,但我建议您阅读
Pixie::Complicity 文档以发现更多奥妙之处。
Pixie 确实有缺点。尽管 Pixie 可使用 RDBMS 作为存储机制,但它没有将您的对象分解成任何一种关系数据。如果您需要匹配对象字段的某个元素,Pixie 可能不是适合您使用的工具。只需给予对象正确的名称,就可以构建相当复杂的索引。例如,如果我希望用 Pixie 来存储我的用户数据库,那么创建一个与我在前面创建的
TodoCollection 相似的集合可能是个不错的主意。
另外,我希望按照我搜索用户名称的方式存储每个有名称的用户。例如,当我希望用户登录时,我需要能够搜索的第一样东西是用户名。因此,有必要将用户对象放入命名的集合中,然后用诸如
user:username:<the users username> 之类的名称将这个集合存储在 Pixie 中。如果我还希望通过用户的电子邮件地址来查找他们,我也会用
user:email:<the users email address> 这样的名称给对象命名。这样的方案提供了通过命名约定获取集合中所有用户及单个用户的方法。通过仔细地创建集合类,您可以产生访存用户分组的能力,但这是一种更具技术性、更特殊的情况。所有这些技术的真正问题依然在于:您不能在以后添加索引。的确,如果您想对数据做临时的报告,Pixie 不是合适的工具。
但是,Pixie 是适合许多其它任务的工具。编写模式或映射往往把程序员束缚在非编程性工作上。太多的情况下,数据库模式是想编写一段软件的助诊文件,如果去除它的话,可以加速开发工作。多年使用 RDMBS 已使得程序员将模式视为必不可少,但通常模式并非如此。Pixie 通过 Perl 来提供帮助,但许多语言都有其适用的 OODBMS 实现,这些实现和那些更传统的机制都应该进行检验。
Pixie 运行在 Perl V5.8.0(以及更高版本)上,在接受 GNU GPL 和 Artistic 许可证的条件下可从所有好的 CPAN 镜像网站获得。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
-
Pixie和
Perl可从 CPAN 获得。
-
Tangram是对象-关系映射器,可用于在关系数据库中持久存储对象。
-
Alzato是数据建模工具,也是对象-关系映射器。
-
SPOPS是允许您序列化对象的一种健壮且功能强大的模块。
- 和 Perl 一样,Pixie 得到了
GNU General Public License(GPL)和
Perl Artistic License(PAL)许可。
-
developerWorks文章“
Python 持久性管理”大致概括了 Python 程序员可用的持久性机制。
-
developerWorks文章“
Mapping objects to relational databases”研究了对象领域和关系领域之间的“阻碍性不匹配(impedence mismatch)”问题。
-
developerWorks文章“
Choosing a database management system”从程序员的角度描述了 DBMS 领域。
- 如需基于教程的方法来安装和使用 Perl DB2 接口,请阅读教程“
使用 Perl 访问 DB2 for Linux”。
- “
The Camel and the Snake, or 'Cheat the Prophet'
Open Source Development with Perl, Python and DB2”介绍了开放源码的一些背景知识,并提到与 DB2 一起使用 Perl 和 Python。
- 在
developerWorksLinux 专区找到更多
适合 Linux 开发人员的参考资料。
James 是 Fotango 的首席科学家,这意味着他大多数时间都在对经典问题应用有趣的新解决方案,以及对有趣的新问题应用经典的解决方案。在业余时间,他在各种 DIY 中屡败屡战。您可以通过 jamesaduncan@mac.com与 James 联系。