Perl的にO/RマッパーはClass::DBI?

データベースにアクセスするようなプログラムを書く時はできるだけSQLを見たくない.接続用のメソッドとかエラーの捕捉用のコードがたくさんあるだけで気分が悪くなってくるほど(言い過ぎ).

やっぱO/Rマッパーでしょ.ちょうどWebDB+PressのバックナンバーのPerlStyleの第2回でClass::DBIのコードを見たので,試してみよう.

次のようなテーブルから一行SELECTする.

CREATE TABLE songs (
  id     INTEGER PRIMARY KEY AUTOINCREMENT,
  name   TEXT,
  singer TEXT,
  album  TEXT,
  works  TEXT
);

いたって普通にDBIを使ってSELECTしてみる.

use strict;
use DBI;

# SQLiteのデータベースにアクセス
my $dbh = DBI->connect("dbi:SQLite:dbname=dbisqlite.db","","", {
    RaiseError => 1,
  }) or die $DBI::errstr;

# SELECT分一行fetch
my $sth = $dbh->prepare('SELECT *  FROM songs');
$sth->execute;
my $name = $sth->fetchrow_hashref->{name};
$sth->finish;

$dbh->disconnect;

print "$name\n";

SQLに関係するコードがほとんどできれいじゃないし,わかりにくいことこの上ない.

つぎもまったく同じ出力が得られる.Class::DBIのおまじないを振りかけたコード.

use strict;
use Song;

my $song = Song->retrieve(1);
my $name = $song->name;
print "$name\n";

すげー短い.さいこう.メインのロジックとは関係ないSQL関係のコードが完璧に隠蔽されてて,わかりやすいことこの上ない.しかしさすがにタネがあって,Songsクラスを次のように定義する必要があるよう.

package Song;
use strict;
use warnings;
use base qw(Class::DBI);

__PACKAGE__->set_db('Main','dbi:SQLite:dbname=dbisqlite.db',"","", {
    RaiseError => 1,
  });

__PACKAGE__->table('songs');
__PACKAGE__->columns(Primary => qw(id));
__PACKAGE__->columns(All => qw(name singer album works));

1;

SQL的な関数も多少はあらわれるけれども,それでもSELECT文は出てこない.Class::DBIのtableというメソッドでテーブル指定して,columnsメソッドで項目の設定をする.すると動的にSongクラスに項目名のメソッドが作成される感じ.かなりスマートだなぁ.あとは,Songsクラスのメソッドを使えばCRUD自由自在.

use strict;
use Song;

# Insert
Song->create({
    name   => 'オハヨウ',
    singer => '双葉&梨々&美森',
    album  => 'オハヨウ',
    works  => '吉永さん家のガーゴイル',
  }) or die "insert fail!";

# Select
my $song = Song->search(name => 'オハヨウ');

# Update
$song->name('愛においで逢いにおいで');
$song->update;

# Delete
$song->delete;               

RubyだとActiveRecordでもっとクールだよ!とかは言わない.