大規模なサイトでは、ORMではSQLレベルのチューニングがしにくいので(Class::DBIで苦労してる)、SQLを直接書くことが多いと思います。その際SQLをいろいろなモジュールに書いてしまうと、あとからALTERやパーティショニングしようとした時に悲劇となります。できるだけ1つのテーブル・データベースのSQLは1つのモジュールにおさめておく事がおすすめです。

DBIx::Sunnyはそのようなモジュールを書く時に使えそうなライブラリで、SQLを管理すると共にクエリ実行時に渡された値のバリデーションもできる様になっています。まだまだ実験中ですが、githubにソースコードあります。

使い方はこんな感じ。まずSQLを書くクラスをDBIx::Sunnyを継承して作ります。query、select_one、select_row、select_allってのがSQLを管理する用の暮らすメソッドとして提供されています。queryは更新系のクエリ用、select_oneは1行目1個目のカラムを取得、select_rowは1行目を取得、select_allは全ての行を取得するメソッドして作成されます。

package MyProj::Data;

use parent 'DBIx::Sunny';
use Mouse::Util::TypeConstraints;

subtype 'Natural'
    => as 'Int'
    => where { $_ > 0 };

subtype 'Uint'
    => as 'Int'
    => where { $_ >= 0 };

no Mouse::Util::TypeConstraints;

__PACKAGE__->query(
    'add_fuga',
    subject => 'Str',
    body => 'Str',
    'INSERT INTO fuga (subject,body) VALUES (?,?)'
);

__PACKAGE__->select_one(
    'count_fuga',
    'SELECT COUNT(*) FROM fuga'
);

__PACKAGE__->select_row(
    'get_data',
    id => 'Natural',
    'SELECT * FROM fuga WHERE id = ?'
);

__PACKAGE__->select_all(
    'get_data_list',
    offset => { isa => 'Uint', default => 0 },
    limit => { isa => 'Uint', default => 10 },
    'SELECT * FROM fuga ORDER BY id DESC LIMIT ?,?'
);

1;

4つのクラスメソッドには、作成するメソッド名、バリデーションルール、SQLの順に渡します。バリデーションルールはData::Validatorの形式となります。なので、Mouse::Util::TypeConstraintsで独自の型を作成して、使う事ができます。DSLっぽいインターフェイスもあれば便利そう。

使うときは、別途接続済みのDBIハンドラをSQLのクラスに渡します。

use MyProj::Data;
use DBI;

# RaiseErrorはおすすめ
my $dbh = DBI->connect('dbi:mysql(RaiseError=>1,PrintError=>0):test');
my $db = MyProj::Data->new(
    dbh => $dbh,
    readonly => 0 # 1にするとqueryメソッドがdieするようになる
);

$result = $db->add_fuga( subject => $subject, body => $body );
ok($result > 0);

# last_insert_idも取れる
my $last_insert_id = $db->last_insert_id;

# カウント
my $count = $db->count_fuga();

# ID指定で1行取得
my $row = $db->get_data( id => 1 );

my $row = $db->get_data( id => 'abc' ); #エラーになる
my $row = $db->get_data( id => 0 ); #エラーになる

# limit, offset付きで取得
my $rows = $db->get_data_list(); LIMIT 0,10
my $rows = $db->get_data_list(limit => 20); LIMIT 0,20
my $rows = $db->get_data_list(offset => 20); LIMIT 20,10

この他に、DBIx::TransactionManagerを利用したトランザクション機能もあります。

基本誰得モジュールなので自分で使ってみつつ、IN (…) とか bulk_insert とかどうしようかなと考え中。ご意見お待ちしています

このブログ記事について

このページは、Masahiro Naganoが2011年1月 7日 19:04に書いたブログ記事です。

ひとつ前のブログ記事は「あけましておめでとうございます」です。

次のブログ記事は「mod_expiresでExpiresとCache-Controlを上書きする」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja