最近Perl.com上的几篇文章(包括Phasebook设计模式)都讨论了Perl代码和数据库打交道的问题。Terrence Brannon的DBIx::Recordset一文试图展示数据库相关的程序也可以更加简单和易于维护。这篇文章是要用Class::DBI来使得这个努力更进一步。
Class::DBI奖励懒惰和简单。目标是使简单的数据库操作几乎不用编程,同时使困难的变得有可能。对很多简单的数据库应用来说,它使我们完全不用编写SQL,另一方面它也不强迫你用很复杂的数据结构来表示一个复杂查询。如果你确实需要原始SQL的功能或表达能力,它也会适时的给你让路。
最容易了解Class::DBI的方法就是用它来建立一个例子程序。这篇文章里面我要做个工具来分析我的电话帐单。
Data::BT::PhoneBill(可在CPAN下载)给我们一个从BT的网站下载电话帐单的方法。有了这个模块和一些最近的通话帐单条目,我们就可以用数据库来存储详细信息以备分析。
Class::DBI的基本概念是数据库中的每个表都有相应的类。尽管每个类都可以自己做连接(数据库)相关的事情,最好还是有个类来把这些事情封装起来。所以我们要建立数据库并为应用程序建立基类:
package My::PhoneBill::DBI;
use base 'Class::DBI';
__PACKAGE__->set_db('Main', 'dbi:mysql:phonebill', 'u/n', 'p/w');
1;
我们只是从Class::DBI继承并用'set_db'方法来建立数据库连接。目前这就是我们在这个类里面需要做的事情,下面我们开始建立用于存储通话信息的表:
CREATE TABLE call (
callid MEDIUMINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(20) NOT NULL,
destination VARCHAR(255) NOT NULL,
calldate DATE NOT NULL,
calltime TIME NOT NULL,
type VARCHAR(50) NOT NULL,
duration SMALLINT UNSIGNED NOT NULL,
cost FLOAT(8,1)
);
为这个我们要建立相应的类:
package My::PhoneBill::Call;
use base 'My::PhoneBill::DBI';
__PACKAGE__->table('call');
__PACKAGE__->columns(All => qw/callid number destination calldate calltime type duration cost/);
1;
我们从基类来继承连接信息,并声明我们要用的表和它包含的列。现在我们要开始填充表里面的数据了。
我们建立了一个简单的名为"populate_phone_bill"的脚本:
#!/usr/bin/Perl
use Data::BT::PhoneBill;
use My::PhoneBill::Call;
my $file = shift or die "Need a phone bill file";
my $bill = Data::BT::PhoneBill->new($file) or die "Can't parse bill";
while (my $call = $bill->next_call) {
My::PhoneBill::Call->create({
number => $call->number,
calldate => $call->date,
calltime => $call->time,
destination => $call->destination,
duration => $call->duration,
type => $call->type,
cost => $call->cost,
});
}
create()调用执行SQL来为每行数据INSERT行。因为我们在使用Class::DBI而且设置了主键为AUTO_INCREMENT,我们就不需要为那个列来提供一个值。对于支持序列的数据库来说,我们也可以提醒Class::DBI需要使用哪个序列来为主键提供下一个唯一值。
现在我们已经有了一个填充了通话数据的表,接着要开始查询数据了。下面就要写个简单的脚本来报告与特定号码的通话记录。
#!/usr/bin/Perl
use My::PhoneBill::Call;
my $number = shift or die "Usage: $0 ";
my @calls = My::PhoneBill::Call->search(number => $number);
my $total_cost = 0;
foreach my $call (@calls) {
$total_cost += $call->cost;
printf "%s %s - %d secs, %.1f pence\n",
$call->calldate, $call->calltime, $call->duration, $call->cost;
}
printf "Total: %d calls, %d pence\n", scalar @calls, $total_cost;
这里看到Class::DBI提供了一个'search'方法给我们用。我们提供一对对的列/值的杂凑来得到所有符合条件的记录。每个记录都是Call对象的一个实例,每个实例也有对应于列名字的存取方法。(这是一个可以调整值的方法,我们可以用来修改记录,但目前我们只关心报表)
有了这个脚本后,如果我们想看看打了报时台几次,就可以运行这个命令
>Perl calls_to 123
2002-09-17 11:06:00 - 5 secs, 8.5 pence
2002-10-19 21:20:00 - 8 secs, 8.5 pence
Total: 2 calls, 17 pence
同样的,若我们想看看某天的所有通话,就可以写个'calls_on'脚本:
#!/usr/bin/Perl
use My::PhoneBill::Call;
my $date = shift or die "Usage: $0 ";
my @calls = My::PhoneBill::Call->search(calldate => $date);
my $total_cost = 0;
foreach my $call (@calls) {
$total_cost += $call->cost;





