=encoding utf8 =head1 NAME DBIx::Custom::Guide::Ja - DBIx::Customの日本語ガイド =head1 ガイド Lはデータベースへのクエリの発行を簡単に行うための クラスです。LやLと同じように Lのラッパクラスになっています。Lよりも簡単に、 Lよりもはるかに柔軟なことを行うことができます。 LはO/Rマッパーではありません。O/Rマッパーは 便利ですが、O/Rマッパのたくさんの文法を覚える必要があります。 また、O/Rマッパによって生成されたSQLは非効率なことがありますし、 複雑なSQLを生成することができないので、 生のSQLを発行しなければならない場合がたくさんあります。 LはO/Rマッパとは対照的な設計が行われています。 Lの主な目的は、SQLを尊重しつつ、Lだけでは とてもめんどうな作業を簡単にすることです。もしSQLについて 多くの知識を持っているならば、Lでそのまま 活用することができます。 Lの仕組みを簡単に説明しておきましょう。 Lでは、タグと呼ばれるものを SQLの中に埋め込むことができます。 select * from book where {= title} and {=author}; {}で囲まれた部分がタグです。このSQLは実際に実行されるときには 次のようにプレースホルダに展開されます。 select * from book where title = ? and author = ?; これらの展開にはどのような意味があるのでしょうかと質問 されることかと思います。この簡単な仕組みの上に非常にたくさんの 有用で便利で使いやすい機能が構築されます。それは以下のようなものです。 =over 4 =item 1. プレースホルダのパラメータをハッシュリファレンスで指定 Lをそのまま使うのであればプレースホルダのパラメータは配列 で指定する必要があります。 $sth->execute(@bind); Lを利用するのであればハッシュリファレンスで指定すること できます。 my $param = {title => 'Perl', author => 'Ken'}; $dbi->execute($sql, $param); =item 2. パラメータのフィルタリング たとえば、日付の列は、Perlで扱うときにはCなどの日付オブジェクト で扱い、データベースに格納するときはデータベースの日付型に変換したい と思うのではないでしょうか。またデータベースから取り出すときは データベースの日付型から日付オブジェクトに変換したと思うのでは ないでしょうか。 このようなときはフィルタ機能を使うことができます。 まずフィルタを登録します。 $dbi->register_filter( tp_to_date => sub { ... }, date_to_tp => sub { ... } ); 次にテーブルの各列にこのフィルタを適用します。 $dbi->apply_filter('book', 'issue_date' => {out => 'tp_to_date', in => 'date_to_tp'} ); outはPerlからデータベースに保存する方向、inはデータベースからPerlに取得する方向です。 SQLを発行するときにテーブルの指定を行えば、自動的にこのフィルタが適用されます。 $dbi->execute($sql, $param, table => 'book'); =item 3. 選択的な検索条件 生のDBIを利用しているとき一番たいへんなのは選択的な検索条件を作成したいときです。 たとえば、検索条件にtitleとauthorが指定された場合は次のSQLを select * from book where title = ? and author = ?; titleだけの場合は次のSQLを select * from book where title = ?; authorだけの場合は次のSQLを発行した場合を考えましょう。 select * from book where author = ?; これはとても大変な作業なので、通常はLを動的に生成してくれる モジュールを利用することになります。 Lはさらに簡単で便利な方法を用意しています。 my $where = $dbi->where; $where->param({title => 'Perl'}); $where->clause( ['and', '{= title}', {'= author'}] ); my $sql = "select * from book $where"; 詳しい説明は後ほど行いますが、上記のように記述すれば、 Lでは選択的な検索条件を持つWhere句を生成することができます。 検索条件が入れ子になった構造やorについても対応しています。 =item 4. 挿入、更新、削除、選択を行うためのメソッド LではSQLをさらに簡単に実行するための メソッドも提供しています。 C, C, C,Cなどの シュガーメソッドを使って、挿入、更新、削除、選択という操作を行うことが できます。 my $param = {title => 'Perl', author => 'Ken'}; $dbi->insert(table => 'book', param => $param); =item 5. テーブル単位の操作の登録 テーブルに対して操作を登録することができます。これによって テーブル名を繰り返し指定する必要がなくなり、ソースコードの 見通しが良くなります。 $dbi->talbe('book', list => sub { ... }, list_somethin => sub { } ); 登録したメソッドはそのまま利用することができます。 $dbi->table('book')->list; 通常O/Rマッパはテーブルに対応するクラスを作成しなければ ならないことが多いですが、Lではこの作業を簡便に しており、上記のように登録することができます。 =back LはLを補うとても便利なモジュールです。 興味をもたれた方は、この後で詳しい解説を行いますので、 ご覧になってみてください。 =head2 1. データベースへの接続 まずLを読み込みます。 use DBIx::Custom; Lオブジェクトを生成し、データベースに接続するには Cメソッドを使用します。 my $dbi = DBIx::Custom->connect( data_source => "dbi:mysql:database=dbname", user => 'ken', password => '!LFKD%$&', dbi_options => {mysql_enable_utf8 => 1} ); Cはデータベースシステムに応じたフォーマットで 指定する必要があります。以下にデータベースごとのフォーマット 方法のサンプルを掲載しておきます。 B "dbi:mysql:database=$database" "dbi:mysql:database=$database;host=$hostname;port=$port" B "dbi:SQLite:dbname=$database" "dbi:SQLite:dbname=:memory:" B "dbi:Pg:dbname=$dbname" B "dbi:Oracle:$dbname" "dbi:Oracle:host=$host;sid=$sid" B "dbi:ODBC:driver=Microsoft Access Driver (*.mdb);dbq=hoge.mdb" B "dbi:ODBC:driver={SQL Server};Server=(local);database=test;Trusted_Connection=yes;AutoTranslate=No;" また認証が求められる場合は、CとCユーザ名と パスワードを指定する必要があります。 LはLのラッパです。 LのデータベースハンドルはCで取得することができます。 my $dbh = $dbi->dbh; Lではデータベースハンドル属性にはデフォルトで次のものが設定されます。 $dbi->dbh->{RaiseError} = 1; $dbi->dbh->{PrintError} = 0; $dbi->dbh->{AutoCommit} = 1; この設定を行っているので、致命的なエラーが起こると、 例外が発生しプログラムは終了します。 またクエリが発行されると自動的にコミットされます。 =head2 2. 挿入、更新、削除、選択のためのメソッド Lは、 C、C、C、C のような挿入、更新、削除、選択を行うためのメソッドを持っています。 簡単なことをを行うのであれば、SQLを自分で記述する必要がありません。 =head3 C データベースにデータを挿入するにはCを使用します。 $dbi->insert(table => 'book', param => {title => 'Perl', author => 'Ken'}); Cにはテーブル名、Cには挿入したいデータを指定します。 次のSQLが発行されます。 insert into (title, author) values (?, ?); =head3 C データベースのデータを更新するには、Cを使用します。 $dbi->update(table => 'book', param => {title => 'Perl', author => 'Ken'}, where => {id => 5}); C
にはテーブル名、Cには挿入したいデータ、Cには 条件を指定します。 次のSQLが発行されます。 update book set title = ?, author = ?; Cメソッドは安全のため where句のないSQLを発行することを許可していません。 もしすべての行を更新したい場合は Cを使用してください。 $dbi->update_all(table => 'book', param => {title => 'Perl', author => 'Ken'}); =head3 C データベースのデータを1件削除するには、Cを使用します。 $dbi->delete(table => 'book', where => {author => 'Ken'}); C
にはテーブル名、Cには条件を指定します。 次のSQLが発行されます。 delete from book where id = ?; Cメソッドは安全のため where句のないSQLを発行することを許可していません。 もしすべての行を削除したい場合は Cを使用してください。 $dbi->delete_all(table => 'book'); =head3 C 行を選択するにはCを使用します。 my $result = $dbi->select(table => 'book'); C
だけを指定して、他の条件を指定しない場合は次のSQLが発行されます。 select * from book; Cメソッドの戻り値はL オブジェクトです。行をフェッチするにはCを使用します。 while (my $row = $result->fetch) { my $title = $row->[0]; my $author = $row->[1]; } Lについてはこの後L<3. 行のフェッチ/"3. 行のフェッチ">で詳しく扱います。 さまざまなCの使い方を見ていきましょう。 次のCだけでなくC、C、C C、C、Cで使用することもできます。 =head4 execute 任意のSQLを実行するにはexecuteメソッドを使用します。 $dbi->execute("select * from book;"); CはLの根幹のメソッドでありタグを展開します。 $dbi->execute( "select * from book {= title} and {= author};" param => {title => 'Perl', author => 'Ken'} ); 上記のタグを含んだSQLは次のように展開されます。 select * from book title = ? and author = ?; SQLが実行されるときにプレースホルダ(?)に対応する位置にtitleとauthor の値がが自動的に埋め込まれます。 タグについてはL<5. タグ/"5. タグ">で詳しく解説しますが、 ひとつの注意点があります。 タグを展開するためにC<{>とC<}>は予約語になっています。 もし利用したい場合は直前に\をおいてエスケープを行う必要があります。 $dbi->execute("... \\{ ... \\} ..."); \自体がPerlのエスケープ文字ですので、二つ必要になるという点に注意してください。 またexecuteのキュートな機能として、SQLの最後にセミコロンをおかなくても かまいません。 $dbi->execute('select * from book'); =head2 3. 行のフェッチ Cメソッドの戻り値はLオブジェクトです。 Lには行をフェッチするためのさまざまなメソッドが 用意されています。 =head3 C 一行フェッチして配列のリファレンスに格納するにはCを使用します。 while (my $row = $result->fetch) { my $title = $row->[0]; my $author = $row->[1]; } whileループを使って、すべての行を取得することができます。 =head3 C 一行だけフェッチして配列のリファレンスに格納するにはC を使用します。 my $row = $result->fetch_first; 一行のフェッチが終わった後はそれ以上フェッチできなくなります。 内部的には1行のフェッチが終わった後に ステートメントハンドルのCが実行されます。 =head3 C 複数行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはCを使用します。 while (my $rows = $result->fetch_multi(2)) { my $title0 = $rows->[0][0]; my $author0 = $rows->[0][1]; my $title1 = $rows->[1][0]; my $author1 = $rows->[1][1]; } 引数には取り出したい行数を指定します。 指定した行を格納した次のようなデータを取得できます。 [ ['Perl', 'Ken'], ['Ruby', 'Mark'] ] =head3 C すべての行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはCを使用します。 my $rows = $result->fetch_all; すべての行を格納した次のようなデータを取得できます。 [ ['Perl', 'Ken'], ['Ruby', 'Mark'] ] =head3 C 一行フェッチしてハッシュのリファレンスに格納するにはCを使用します。 while (my $row = $result->fetch_hash) { my $title = $row->{title}; my $author = $row->{author}; } =head3 C 一行だけフェッチしてハッシュのリファレンスに格納するには Cを使用します。 my $row = $result->fetch_hash_first; 一行のフェッチが終わった後はそれ以上フェッチできなくなります。 内部的には1行のフェッチが終わった後に ステートメントハンドルのCが実行されます。 =head3 C 複数行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはC を使用します。 while (my $rows = $result->fetch_hash_multi(5)) { my $title0 = $rows->[0]{title}; my $author0 = $rows->[0]{author}; my $title1 = $rows->[1]{title}; my $author1 = $rows->[1]{author}; } 引数には取り出したい行数を指定します。 指定した行を格納した次のようなデータを取得できます。 [ {title => 'Perl', author => 'Ken'}, {title => 'Ruby', author => 'Mark'} ] =head3 C すべての行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはC を使用します。 my $rows = $result->fetch_hash_all; すべての行を格納した次のようなデータを取得できます。 [ {title => 'Perl', author => 'Ken'}, {title => 'Ruby', author => 'Mark'} ] =head3 sth ステートメントハンドルに直接アクセスしたい場合は で取得することができます。 my $sth = $result->sth; フェッチのパフォーマンスが用件を満たさないときには、 ステートメントハンドルから 利用できる速度の速いメソッドを利用することができます。 =head2 4. フィルタリング データベースにデータを登録するときやデータベースからデータを取得する ときに自動的に値の変換を行いたい場合が多いと思います。 たとえば、日付を表現する列の場合は、 データベースに登録する場合はLオブジェクトから データベースの日付のフォーマットに、 データベースからデータを取得するときは、その逆を行えると便利です。 =head3 フィルタの登録 フィルタを登録するにはCを使用します。 $dbi->register_filter( # Time::Piece object to DATE format tp_to_date => sub { my $date = shift; return '0000-00-00' unless $tp; return $tp->strftime('%Y-%m-%d'); }, # DATE to Time::Piece object date_to_tp => sub { my $date = shift; return if $date eq '0000-00-00'; return Time::Piece->strptime($date, '%Y-%m-%d'); }, ); 登録したフィルタはCなどで利用することができます。 =head3 フィルタの適用 作成したフィルタを適用するには、Cを使用します。 $dbi->apply_filter('book', issue_date => {out => 'tp_to_date', in => 'date_to_tp'}, first_issue_date => {out => 'tp_to_date', in => 'date_to_tp'} ); 第一引数はテーブル名です。第二引数以降は、列名とフィルタルールのペアを記述します。 フィルタルールのoutには、データベースにデータを送信するときに適用するフィルタを、 フィルタルールのinには、データベースからデータを取得するときに適用するフィルタを 記述します。outがデータベースに送信する方向、inがデータベースから取り出す方向です。 フィルタには、Cで登録したフィルタ名の他に、コードリファレンスを 指定することもできます。 issue_date => {out => sub { ... }, in => sub { ... }} 適用されたフィルタはC、C、C、C、 C、Cで有効になります。 my $tp = Time::Piece->strptime('2010/10/14', '%Y/%m/%d'); my $result = $dbi->select(table => 'book', where => {issu_date => $tp}); データベースにデータが送信されるときに、Lオブジェクトは データベースの日付のフォーマット「2010-10-14」に変換されます。 また逆にデータをフェッチするときには、データベースの日付のフォーマットは タイムピースオブジェクトに変換されます。 my $row = $resutl->fetch_hash_first; my $tp = $row->{issue_date}; このような自動的に実行されるフィルタを登録できることがLの 特徴のひとつです。 =head3 個別のフィルタの適用 Cを使って最初にすべてのテーブルの列について フィルタを定義することもできますが、 個別にフィルタを適用することもできます。 個別のフィルタはCで適用したフィルタを上書きます。 個別のフィルタはSQLのasを使って、列の別名を作成する必要がある場合に活躍します。 データベースに送信する場合に、個別のフィルタを適用するには、各メソッドの Cオプションを使用します。個別のフィルタは、C、C、 C、C、C、C、C で使用することができます。 Cの例を示します。 $dbi->insert( table => 'book', param => {issue_date => $tp, first_issue_date => $tp}, filter => {issue_date => 'tp_to_date', first_issue_date => 'tp_to_date'} ); Cの例を示します。 my $sql = <<"EOS"; select YEAR(issue_date) as issue_year from book where YEAR(issue_date) = {? issue_year} EOS my $result = $dbi->execute( $sql, param => {issue_year => '2010'}, filter => {issue_year => 'tp_to_year'} ); これはCを使う良くある例です。issue_dateの変換についてはC で登録してあるのですが、新しく作成した列であるissue_yearについては、 何の変換も登録されていません。ですので、個別にフィルタを設定しています。 また反対に行をフェッチするときにも個別のフィルタを適用することができます。 フィルタを適用するには、 CクラスのCメソッドを使用します。 $result->filter(issue_year => 'year_to_tp'); 頻繁に利用するのであれば、個別に登録するよりもCで登録 しておいたほうが便利でしょう。Cは存在しない列に対しても フィルタを適用できるからです。 $dbi->apply_filter('book', 'issue_year' => {out => 'tp_to_year', in => 'year_to_tp'} ); Cではさらに最後にもう一度、フィルタを追加で 登録することができます。たとえばHTMLに出力したい場合に、Time::Piece オブジェクトから読みやすい記述に変換することができます。 最後のフィルタを登録するには、Cを使用します。 $result->end_filter(issue_date => sub { my $tp = shift; return '' unless $tp; return $tp->strftime('%Y/%m/%d %h:%m:%s (%a)'); }); 日付を見やすい形にフォーマットすることができます。 フィルタはフェッチを行う前に登録しておく必要があることに 注意してください。 $result->filter(...); $result->end_filter(...); my $row = $result->fetch_hash_first; =head3 列の情報を元にフィルタを適用する 日付型の列は手動で設定しなくても、自動的に設定できると便利です。 このためにデータベースのテーブルの列のすべての情報を 順番に処理するためのCがあります。 $dbi->each_column( sub { my ($self, $table, $column, $info) = @_; my $type = $info->{TYPE_NAME}; my $filter = $type eq 'DATE' ? {out => 'tp_to_date', in => 'date_to_tp'} : $type eq 'DATETIME' ? {out => 'tp_to_datetime', in => 'datetime_to_tp'} : undef; $self->apply_filter($table, $column, $filter) if $filter; } ); each_columnはコールバックを受け取ります。コールバックの引数は 順番にLオブジェクト、テーブル名、列名、列の情報です。 列の型名の情報をもとに自動的に、フィルタを適用しています。 ひとつの注意点としてコールバックの中から、コールバックの外側 の変数を参照しないように注意してください。each_columnは 高々1回だけ実行されるだけなので、ほとんどの場合問題ありませんが、 循環参照によるメモリリークが発生してしまう可能性を持っているからです。 =head2 5. タグ Lはハッシュパラメタバインドを提供します。 まず最初にLによる通常のパラメタバインドをご覧ください。 use DBI; my $dbh = DBI->connect(...); my $sth = $dbh->prepare( "select * from book where author = ? and title like ?;" ); $sth->execute('Ken', '%Perl%'); これはデータベースシステムがSQLをキャッシュすることができ、 パラメータは自動的にクォートされるので、 パフォーマンス面でも、セキュリティ面でも とても良い方法です。 Lはこれを改善して、ハッシュで パラメタを指定できるようにしました。 my $result = $dbi->execute( "select * from book where {= author} and {like title};" param => {author => 'Ken', title => '%Perl%'} ); C<{= author}>とC<{like title}>はタグと呼ばれます。 タグは内部ではプレースホルダを含む文字列に置き換えられます。 select * from book where {= author} and {like title} という文は以下のSQLに置き換えられます。 select * from book where author = ? and title like ?; このようにタグを使ってSQL文を表現するのがLの 特徴です。以下のタグが利用可能です。 [TAG] [REPLACED] {? NAME} -> ? {= NAME} -> NAME = ? {<> NAME} -> NAME <> ? {< NAME} -> NAME < ? {> NAME} -> NAME > ? {>= NAME} -> NAME >= ? {<= NAME} -> NAME <= ? {like NAME} -> NAME like ? {in NAME COUNT} -> NAME in [?, ?, ..] {insert_param NAME1 NAME2} -> (NAME1, NAME2) values (?, ?) {update_param NAME1 NAME2} -> set NAME1 = ?, NAME2 = ? これらの変換はLによって行われます。 C<{>とC<}>は予約語です。これらの文字を使いたい場合は 「\」を使ってエスケープする必要があります。 '\'はPerlのエスケープ文字なので、 エスケープするためには'\\'と書く必要があることに注意 してください。 'select * from book \\{ something statement \\}' =head3 Lの機能の拡張 新しいタグが欲しい場合はLの機能を拡張 することができます。 my $dbi = DBIx::Custom->connect(...); $dbi->register_tag( name => sub { ... } ); =head2 6. Where句の動的な生成 =head2 7. パフォーマンスの改善 =head3 シュガーメソッドを使わない もしCメソッドを使用してインサートを実行した場合、 必要なパフォーマンスを得られない場合があるかもしれません。 Cメソッドは、SQL文とステートメントハンドルを 毎回作成するためすこし遅いです。 そのような場合は、Cメソッドによって クエリを用意しておくことができます。 my $query = $dbi->create_query( "insert into book {insert_param title author};" ); 戻り値はLオブジェクトです。 このオブジェクトはSQL文とパラメータバインド時の列名を 保持しています。またステートメントハンドルも保持しています。 { sql => 'insert into book (title, author) values (?, ?);', columns => ['title', 'author'], sth => $sth } クエリオブジェクトを使って繰り返し実行するには次のようにします。 my $inputs = [ {title => 'Perl', author => 'Ken'}, {title => 'Good days', author => 'Mike'} ]; foreach my $input (@$inputs) { $dbi->execute($query, $input); } Cメソッドの第一引数にクエリオブジェトを渡すことができます。 これはCメソッドよりも高速です。 =head2 8. その他の機能 =head3 トランザクション トランザクションを便利に利用するために、 C、C、C という三つのメソッドが容易されています。 これはLの同名のメソッドと同じ機能を持ちます。 fc $dbi->begin_work; eval { $dbi->update(...); $dbi->update(...); }; if ($@) { $dbi->rollback; } else { $dbi->commit; } =head3 selectメソッドの結果クラスの変更 必要ならばCメソッドの結果クラスを変更することができます。 package Your::Result; use base 'DBIx::Custom::Result'; sub some_method { ... } 1; package main; use Your::Result; my $dbi = DBIx::Custom->connect(...); $dbi->result_class('Your::Result'); =head3 ヘルパーメソッドの登録 ヘルパーメソッドを登録することができます。 $dbi->helper( update_or_insert => sub { my $self = shift; # do something }, find_or_create => sub { my $self = shift; # do something } ); メソッドで登録したメソッドは Lオブジェクトから直接呼び出すことができます。 $dbi->update_or_insert; $dbi->find_or_create; =head3 ユーティリティメソッド Cメソッドを使用すると次のようなハッシュに含まれる テーブル名と列名を結合することができます。 my %expanded = $dbi->expand(\%source); 以下のハッシュ {book => {title => 'Perl', author => 'Ken'}} は次のように展開されます。 ('book.title' => 'Perl', 'book.author' => 'Ken') これはテーブル名を含むselect文で利用すると便利です。 my $param = {title => 'Perl', author => '%Ken%'}; $dbi->execute( 'select * from book where {= book.title} && {like book.author};', param => {$dbi->expand({book => $param})} ); =cut