DBIx::Custom::Guide::Ja - DBIx::Customのガイドブック
(このガイドは将来的なもので実験的な機能についても記述されています。 DBIx::CustomのPODにEXPERIMENTALとマークされている ものは実験的な機能です。)
DBIx::CustomはSQLの実行を簡単に行うためのクラスです。 DBIx::ClassやDBIx::Simpleと同じように DBIのラッパクラスになっています。DBIx::Classよりも簡単に、 DBIx::Simpleよりもはるかに柔軟なことを行うことができます。
DBIx::CustomはO/Rマッパーではありません。O/Rマッパーは 便利ですが、O/Rマッパのたくさんの文法を覚える必要があります。 また、O/Rマッパによって生成されたSQLは非効率なことがありますし、 複雑なSQLを生成することができないので、 生のSQLを実行しなければならない場合がたくさんあります。
DBIx::CustomはO/Rマッパとは対照的な設計が行われています。 DBIx::Customの主な目的は、SQLを尊重しつつ、DBIだけでは とてもめんどうな作業を簡単にすることです。もしSQLについて 多くの知識を持っているならば、DBIx::Customでそのまま 活用することができます。
DBIx::Customの仕組みを少しだけ説明しておきます。 DBIx::Customでは、タグと呼ばれるものを SQLの中に埋め込むことができます。
select * from book where {= title} and {=author};
{}で囲まれた部分がタグです。このSQLは実際に実行されるときには 次のようにプレースホルダに展開されます。
select * from book where title = ? and author = ?;
これらの展開にはどのような意味があるのでしょうかと質問 されるかもしれません。この簡単な仕組みの上に 便利な機能が実装されます。それは以下のようなものです。
DBIを使うのであればプレースホルダにバインドする値は配列 で指定する必要があります。
$sth->execute(@bind);
DBIx::Customを利用するのであればハッシュリファレンスで指定すること できます。
my $param = {title => 'Perl', author => 'Ken'}; $dbi->execute($sql, $param);
DBIx::Customはフィルタリングの機能を提供します。 たとえば、日付の列は、Perlで扱うときにはTime::Pieceなどの日付オブジェクト で扱い、データベースに格納するときはデータベースの日付型に変換したい と思うのではないでしょうか。またデータベースから取り出すときは データベースの日付型から日付オブジェクトに変換したと思うのでは ないでしょうか。
Time::Piece
このようなときはフィルタ機能を使うことができます。
まずフィルタを登録します。
$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に取得する方向です。
多くのメソッドで自動的にこのフィルタが有効になります。
$dbi->insert(table => 'book', param => {issue_date => $tp});
DBIでは選択的に検索条件を作成することは難しいです。
たとえば、検索条件にtitleとauthorが指定された場合は次のSQLを
titleだけの場合は次のSQLを
select * from book where title = ?;
authorだけの場合は次のSQLを実行した場合を考えましょう。
select * from book where author = ?;
これはとても大変な作業なので、通常はSQL::Abstractを動的に生成してくれる モジュールを利用することになります。
DBIx::Customはさらに簡単で便利な方法を用意しています。
# Whereオブジェクト my $where = $dbi->where; # 検索条件 $where->clause( ['and', '{= title}', {'= author'}] ); # 必要な列を自動的に選択するための設定 $where->param({title => 'Perl'}); # SQLへのWhere句の埋め込み my $sql = "select * from book $where";
詳しい説明は後ほど行いますが、上記のように記述すれば、 DBIx::Customでは選択的な検索条件を持つWhere句を生成することができます。 検索条件が入れ子になった構造やorについても対応しています。
DBIx::Customでは挿入、更新、削除、選択を行うための メソッドを提供しています。 insert(), update(), delete(),select()などがあります。
insert()
update()
delete()
select()
my $param = {title => 'Perl', author => 'Ken'}; $dbi->insert(table => 'book', param => $param);
テーブルのためにメソッドを登録することができます。
$dbi->table('book')->method( list => sub { ... }, something => sub { ... } );
メソッドの利用です。
$dbi->table('book')->list;
多くのO/Rマッパではテーブルのためのクラスを作成する必要がありますが、 DBIx::Customでは簡単です。
DBIx::Customはとても便利です。 興味をもたれた方は、続きをご覧になってみてください。
DBIx::Customを読み込みます。
use DBIx::Custom;
データベースに接続するにはconnect()メソッドを使用します。 戻り値はDBIx::Customオブジェクトです。
connect()
my $dbi = DBIx::Custom->connect( data_source => "dbi:mysql:database=bookstore", user => 'ken', password => '!LFKD%$&', dbi_options => {mysql_enable_utf8 => 1} );
data_sourceはデータベースシステムに応じたものである必要があります。 以下はデータソースのサンプルです。
data_source
MySQL
"dbi:mysql:database=$database" "dbi:mysql:database=$database;host=$hostname;port=$port"
SQLite
"dbi:SQLite:dbname=$database" "dbi:SQLite:dbname=:memory:"
PostgreSQL
"dbi:Pg:dbname=$dbname"
Oracle
"dbi:Oracle:$dbname" "dbi:Oracle:host=$host;sid=$sid"
ODBC(Microsoft Access)
"dbi:ODBC:driver=Microsoft Access Driver (*.mdb);dbq=hoge.mdb"
ODBC(SQL Server)
"dbi:ODBC:driver={SQL Server};Server=(local);database=test;Trusted_Connection=yes;AutoTranslate=No;"
認証が必要な場合は、userとpasswordを指定できます。
user
password
DBIx::CustomはDBIのラッパークラスです。 DBIのデータベースハンドルは取得するにあhdbh()を使用します。
dbh()
my $dbh = $dbi->dbh;
DBIx::Customではデータベースハンドル属性にはデフォルトで次のものが設定されます。
$dbi->dbh->{RaiseError} = 1; $dbi->dbh->{PrintError} = 0; $dbi->dbh->{AutoCommit} = 1;
致命的なエラーが起こるとプログラムは終了します。 SQLが実行されると自動的にコミットされます。
下記のメソッドがあります。
データベースに行を挿入するにはinsert()を使用します。
$dbi->insert(table => 'book', param => {title => 'Perl', author => 'Ken'});
tableはテーブル名、paramは挿入する行のデータです。
table
param
次のSQLが実行されます。
insert into (title, author) values (?, ?);
データベースの行を更新するには、update()を使用します。
$dbi->update(table => 'book', param => {title => 'Perl', author => 'Ken'}, where => {id => 5});
tableはテーブル名、paramは更新データ、whereは 条件です。
where
update book set title = ?, author = ?;
安全のためwhereのないupdate()を実効することはできません。 もしすべての行を更新したい場合は update_all()を使用してください。
update_all()
$dbi->update_all(table => 'book', param => {title => 'Perl', author => 'Ken'});
データベースの行を1件削除するには、delete()を使用します。
$dbi->delete(table => 'book', where => {author => 'Ken'});
tableはテーブル名、whereは条件です。
delete from book where id = ?;
安全のためwhereのないdelete()を実効することはできません。 もしすべての行を削除したい場合は delete_all()を使用してください。
delete_all()
$dbi->delete_all(table => 'book');
行を選択するにはselect()を使用します。
my $result = $dbi->select(table => 'book');
select * from book;
戻り値はDBIx::Custom::Result オブジェクトです。行をフェッチするにはfetch()を使用します。
fetch()
while (my $row = $result->fetch) { my $title = $row->[0]; my $author = $row->[1]; }
DBIx::Custom::Resultについては"3. 行のフェッチ" in 3. 行のフェッチを見てください。
サンプルを続けます。
my $result = $dbi->select( table => 'book', column => ['author', 'title'], where => {author => 'Ken'} );
columnは列名、whereは条件です。
column
select author, title from book where author = ?;
次のサンプルです。
my $result = $dbi->select( table => 'book', column => ['company.name as company__name'] where => {'book.name' => 'Perl'}, join => ['left outer join company on book.company_id = company.id] );
joinでテーブルの結合を行うことができます。
join
select company.name as company__name from book left outer join company on book.company_id = company.id where book.name = ?;
bookテーブルのcompany_id列とcompanyテーブルのidが左外部結合されます。 次のSQLが実行されます。
joinされるのは、whereやcolumnにテーブル名が含まれている 場合だけであることに注意してください。 次のように指定した場合は結合の必要はないと判断されjoinはされません。
my $result = $dbi->select( table => 'book', where => {'name' => 'Perl'}, join => ['left outer join company on book.company_id = company.id] );
select * from book where book.name = ?;
my $result = $dbi->select( table => 'book', where => {author => 'Ken'}, append => 'for update', );
appendはSQLの末尾に追加される文字列です。
append
select * book where author = ? for update;
またappendは、selectだけでなくinsert()、update()、update_all() delete()、delete_all()、select()で使用することもできます。
select
execute()
SQLを実行するにはexecute()を使用します。
$dbi->execute("select * from book;");
タグを処理してSQLを実行します。
$dbi->execute( "select * from book {= title} and {= author};" param => {title => 'Perl', author => 'Ken'} );
select * from book title = ? and author = ?;
プレースホルダにtitleとauthorの値が埋め込まれます。
タグについては"5. タグ" in 5. タグを見てください。
またexecute()のSQLの末尾にはセミコロンを置く必要はありません。
$dbi->execute('select * from book');
insert_at()
プライマリーを使用して行を更新するにはinsert_at()を使用します。
$dbi->insert_at( table => 'book', primary_key => ['id'], where => ['123'], param => {name => 'Ken'} );
この例ではidの列が123の行に挿入されます。whereには、配列の リファレンスを渡す必要があることに注意してください。 なおparamにプライマリーキーが含まれていた場合は、そのキーが削除されます。
update_at()
プライマリーを使用して行を更新するにはupdate_at()を使用します。
$dbi->update_at( table => 'book', primary_key => ['id'], where => ['123'], param => {name => 'Ken'} );
この例ではidの列が123の行が更新されます。whereには、配列の リファレンスを渡す必要があることに注意してください。 なおparamにプライマリーキーが含まれていた場合は、そのキーが削除されます。
delete_at()
プライマリーを使用して行を削除するにはdelete_at()を使用します。
$dbi->delete_at(table => 'book', primary_key => ['id'], where => ['123']);
この例ではidの列が123の行が削除されます。whereには、配列の リファレンスを渡す必要があることに注意してください。
また下のような記述方法も許されています。
$dbi->delete_at(table => 'book', primary_key => ['id'], param => {id => '123'});
select_at()
プライマリーを使用して行を選択するにはselect_at()を使用します。
$dbi->select_at(table => 'book', primary_key => ['id'], where => ['123']);
この例ではidの列が123の行が選択されます。whereには、配列の リファレンスを渡す必要があることに注意してください。
$dbi->select_at(table => 'book', primary_key => ['id'], param => {id => '123'});
select()メソッドの戻り値はDBIx::Custom::Resultオブジェクトです。 行をフェッチするためのさまざまなメソッドがあります。
一行フェッチして配列のリファレンスに格納するにはfetch()を使用します。
my $row = $result->fetch;
以下のようにすべての行を取得することができます。
fetch_first()
一行だけフェッチして配列のリファレンスに格納するにはfetch_first() を使用します。
my $row = $result->fetch_first;
ステートメントハンドルのfinish()が実行される ので残りの行をフェッチできません。
finish()
fetch_multi()
複数行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはfetch_multi()を使用します。
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'] ]
fetch_all
すべての行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはfetch_all()を使用します。
fetch_all()
my $rows = $result->fetch_all;
すべての行を格納した次のようなデータを取得できます。
fetch_hash()
一行フェッチしてハッシュのリファレンスに格納するにはfetch_hash()を使用します。
while (my $row = $result->fetch_hash) { my $title = $row->{title}; my $author = $row->{author}; }
fetch_hash_first()
一行だけフェッチしてハッシュのリファレンスに格納するには fetch_hash_first()を使用します。
my $row = $result->fetch_hash_first;
fetch_hash_multi()
複数行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはfetch_hash_multi() を使用します。
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'} ]
fetch_hash_all()
すべての行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはfetch_hash_all() を使用します。
my $rows = $result->fetch_hash_all;
sth()
ステートメントハンドル取得したい場合は <sth()>を使用します。
my $sth = $result->sth;
DBIx::Customは値のフィルタリング機能を提供します。
たとえば、データをデータベースに登録するときは Time::Pieceオブジェクトからデータベースの日付のフォーマットに、 データベースからデータを取得するときは、 データベースの日付のフォーマットからTime::Pieceオブジェクト に変換を行いたいと思うことでしょう。
register_filter()
フィルタを登録するにはregister_filter()を使用します。
$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'); }, );
登録したフィルタはapply_filter()などで利用することができます。
apply_filter()
作成したフィルタを適用するには、apply_filter()を使用します。
$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'} );
第一引数はテーブル名です。第1引数より後の引数は、列名とフィルタルールのペアを記述します。 フィルタルールのoutには、データベースにデータを送信するときに適用するフィルタを、 フィルタルールのinには、データベースからデータを取得するときに適用するフィルタを 記述します。
フィルタとしてコードリファレンスを 指定することもできます。
issue_date => {out => sub { ... }, in => sub { ... }}
適用されたフィルタはinsert()、update()、update_all()、delete()、 delete_all()、select()で有効になります。
my $tp = Time::Piece->strptime('2010/10/14', '%Y/%m/%d'); my $result = $dbi->select(table => 'book', where => {issu_date => $tp});
データベースにデータが送信されるときに、Time::Pieceオブジェクトは データベースの日付のフォーマット「2010-10-14」に変換されます。
データをフェッチするときには、データベースの日付のフォーマットは Time::Pieceオブジェクトに変換されます。
my $row = $resutl->fetch_hash_first; my $tp = $row->{issue_date};
テーブル名を含む列名を使用することもできます。
$dbi->select( table => 'book', where => {'book.title' => 'Perl', 'book.author' => 'Ken'} );
フェッチを行う場合に"TABLE__COLUMN"という名前を使用した場合もフィルタは 有効になります。
my $result = $dbi->execute( "select issue_date as book__issue_date from book");
inフィルタの後に実行されるendフィルタを適用することもできます。
in
end
$dbi->apply_filter('book', issue_date => {out => 'tp_to_date', in => 'date_to_tp', end => 'tp_to_displaydate'}, );
filter
個別にフィルタを適用することもできます。 個別のフィルタはapply_filter()で適用したフィルタを上書きます。
データを送信する場合に個別のフィルタを適用するには、filterオプションを使用します。 このオプションはinsert()、update()、 update_all()、delete()、delete_all()、select()、execute() で使用することができます。
$dbi->insert( table => 'book', param => {issue_date => $tp, first_issue_date => $tp}, filter => {issue_date => 'tp_to_date', first_issue_date => 'tp_to_date'} );
execute()の例を示します。
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'} );
行をフェッチするときにも個別のフィルタを適用することができます。 DBIx::Custom::Resultのfilter()を使用します。
DBIx::Custom::Result
filter()
$result->filter(issue_year => 'year_to_tp');
remove_filter()でフィルタを取り除くこともできます。
remove_filter()
$result->remove_filter
end_filter()
最後にもうひとつフィルタを追加することができます。 最終的な出力を作成する場合に便利です。 最後のフィルタを登録するにはend_filter()を使用します。
$result->end_filter(issue_date => sub { my $tp = shift; return '' unless $tp; return $tp->strftime('%Y/%m/%d %h:%m:%s (%a)'); });
この例ではTime::Pieceオブジェクトを読みやすい書式に変換しています。
最後のフィルタリングをremove_filter()で取り除くこともできます。
$result->remove_end_filter;
each_column()
日付型の列は自動的にフィルタを適用できると便利です。 列のすべての情報を処理するためのeach_column()を利用することができます。
$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はコールバックを受け取ります。コールバックの引数は DBIx::Customオブジェクト、テーブル名、列名、列の情報です。 列の型名の情報をもとに自動的に、フィルタを適用しています。
SQLの中にタグを埋め込むことができます。
select * from book where {= title} and {like author};
{= title}と{like author}の部分がタグです。タグは次のような形式 を持ちます。
{タグ名 引数1 引数2 ...}
タグは{で始まり、}で終わります。最初の{とタグ名の間 には空白を挿入しないよう注意してください。
{
}
{と}は予約語になっています。 もし利用したい場合は\でエスケープを行う必要があります。
select from book \\{ ... \\}
\自体がPerlのエスケープ文字ですので、 \は二つ必要になります。
\
タグはSQLが実行される前に展開されます。
select * from book where title = ? and author like ?;
タグを含むSQLを実行するにはexecute()を使用します。
my $sql = "select * from book where {= author} and {like title};" $dbi->execute($sql, param => {title => 'Perl', author => '%Ken%'});
paramオプションを使って、プレースホルダに埋め込みたい値を ハッシュリファレンスで指定することができます。
execute()においてもfilterを指定することができます。
$dbi->execute($sql, param => {title => 'Perl', author => '%Ken%'} filter => {title => 'to_something');
executeではapply_filter()で適用されたフィルタ は有効ではないということに注意してください。 apply_filter()で適用されたフィルタを有効にするには、 tableタグを利用します。
execute
my $sql = "select * from {table book} where {= author} and {like title};"
{table NAME} -> NAME
これはSQLの中でテーブル名を指定する場合に利用します。 テーブル名を指定することによって、apply_filter() によるフィルタリングが有効になります。
?
{? NAME} -> ?
=
{= NAME} -> NAME = ?
<>
{<> NAME} -> NAME <> ?
<
{< NAME} -> NAME < ?
>
{> NAME} -> NAME > ?
>=
{>= NAME} -> NAME >= ?
<=
{<= NAME} -> NAME <= ?
like
{like NAME} -> NAME like ?
{in NAME COUNT} -> NAME in [?, ?, ..]
insert_param
{insert_param NAME1 NAME2} -> (NAME1, NAME2) values (?, ?)
update_param
{update_param NAME1 NAME2} -> set NAME1 = ?, NAME2 = ?
同名の列を含むタグがある場合でも大丈夫です。 二つの日付で比較しなければならない場合を 考えて見ましょう。
my $sql = "select * from table where {> date} and {< date};";
このような場合はパラメータの値を配列のリファレンスで指定します。
my $dbi->execute($sql, param => {date => ['2010-10-01', '2012-02-10']});
register_tag()
独自のタグを登録することができます。 タグを追加するにはregister_tag()を使用します。
$dbi->register_tag( '=' => sub { my $column = shift; return ["$column = ?", [$column]]; } );
ここではデフォルトの=タグがどのように実装されているかを示しています。 タグの形式は次のようになっています。
=タグの場合は
{= title}
という形式ですから、サブルーチンにはtitleというひとつの列名がわたってきます。
サブルーチンの戻り値には次の形式の配列のリファレンスを返す必要があります。
[ 展開後の文字列, [プレースホルダに埋め込みに利用する列名1, 列名2, ...] ]
一つ目の要素は展開後の文字列です。この例では
'title = ?'
を返す必要があります。
二つ目の要素はプレースホルダに埋め込みに利用する列名を含む配列の リファレンスです。今回の例では
['title']
を返す必要があります。複数のプレースホルダを含む場合は、この部分が 複数になります。
上記を合わせると
['title = ?', ['title']]
を返す必要があるということです。
タグの実装の他のサンプルはDBIx::Custom::Tagのソースコード をご覧になってみてください。
複数の検索条件を指定して、検索を行いたい場合があります。 次の3つのケースのwhere句を考えてみましょう。
titleの値だけで検索したい場合
where {= title}
authorの値だけで検索したい場合
where {= author}
titleとauthorの両方の値で検索したい場合
where {= title} and {=author}
DBIx::Customでは動的なWhere句の生成をサポートしています。 まずwhere()でDBIx::Custom::Whereオブジェクトを生成します。
where()
my $where = $dbi->where;
次にclause()を使用してwhere句を記述します。
clause()
$where->clause( ['and', '{= title'}, '{= author}'] );
clauseの指定方法は次のようになります。
['or' あるいは 'and', タグ1, タグ2, タグ3]
第一引数にはorあるいはandを指定します。第二引数以降には 検索条件をタグを使って記述します。
clauseの指定は入れ子にすることもでき、さらに複雑な条件 を記述することもできます。
clause
['and', '{= title}', ['or', '{= author}', '{like date}'] ]
これは "{=title} and ( {=author} or {like date} )" 意味しています。
clauseを設定した後にparamにパラメータを指定します。
$where->param({title => 'Perl'});
この例ではtitleだけがパラメータに含まれています。
この後to_string()を実行すると$paramに含まれるパラメータを満たす where句を生成することができます。
to_string()
my $where_clause = $where->to_string;
パラメータはtitleだけですので、次のようなwhere句が生成されます。
またDBIx::Customは文字列の評価をオーバーロードして、to_string() を呼び出すようにしていますので、次のようにしてwhere句を生成することも できます。
my $where_clause = "$where";
これはSQLの中にwhere句を埋め込むときにとても役立つ機能です。
タグの中に同一の名前を持つものが存在した場合でも動的に where句を作成することができます。
たとえば、パラメータとして開始日付と終了日付を受け取ったことを 考えてみてください。
my $param = {start_date => '2010-11-15', end_date => '2011-11-21'};
この場合はパラメータの値を配列のリファレンスにしてください。
my $p = {date => ['2010-11-15', '2011-11-21']};
同名の列を含むタグに順番に埋め込むことができます。
$where->clause( ['and', '{> date}', '{< date}'] ); $where->param($p);
また開始日付が存在しない場合は次のようなデータを作成します。
my $p = {date => [$dbi->not_exists, '2011-11-21']};
not_exists()でDBIx::Custom::NotExistsオブジェクトを 取得できます。これは対応する値が存在しないことを示すためのものです。
not_exists()
また終了日付が存在しない場合は次のようなデータを作成します。
my $p = {date => ['2010-11-15']};
どちらも存在しない場合は次のようなデータを作成します。
my $p = {date => []};
少し難しいので一番簡単に作成できるロジックを示しておきます。
my @date; push @date, exists $param->{start_date} ? $param->{start_date} : $dbi->not_exists; push @date, $param->{end_date} if exists $param->{end_date}; my $p = {date => \@date};
DBIx::Custom::Whereオブジェクトは select()のwhereに直接渡すことが できます。
my $where = $dbi->where; $where->clause(...); $where->param($param); my $result = $dbi->select(table => 'book', where => $where);
あるいはupdate()、delete()のwhereに指定することも可能です。
execute()との連携です。SQLを作成するときに埋め込むことができます。
my $where = $dbi->where; $where->clause(...); $where->param($param); my $sql = <<"EOS"; select * from book; $where EOS $dbi->execute($sql, param => $param);
ソースコードの見通しをよくするために、 DBIx::Custom::Modelを継承してモデルを作成することができます。
まず最初にモデルの元になるクラスを<DBIx::Custom::Model> を継承して作成します。
package MyModel; use base 'DBIx::Custom::Model';
次に個々のモデルクラスを作成します。
MyModel::book
package MyModel::book; use base 'MyModel'; sub insert { ... } sub list { ... }
MyModel::company
package MyModel::company; use base 'MyModel'; sub insert { ... } sub list { ... }
このように作成したモジュールを次のように配置してください。
MyModel.pm MyModel / book.pm / company.pm
このように作成したモデルはinclude_model()で取り込むことができます。
include_model()
$dbi->include_model('MyModel');
第一引数は、モデルの名前空間になります。
モデルは次のように利用することができます。
my $result = $dbi->model('book')->list;
モデルではテーブル名を指定することなしに insert(), update(), update_all(), delete(), delete_all(), select()などのメソッドを 利用できます。
$dbi->model('book')->insert(param => $param);
またモデルクラスでprimary_keyの設定がなされていれば、 プライマリキーを指定することなしに insert_at, update_at(), delete_at(), select_at()のメソッドを 利用できます。
primary_key
insert_at
$dbi->model('book')->delete_at(where => 123);
モデルはDBIx::Custom::Modelです。
必要であれば、table()でテーブル名を取得することができます。
table()
my $table = $model->table;
DBIx::Customオブジェクトを取得することもできます。
my $dbi = $model->dbi;
DBIx::CustomとDBIのすべてのメソッドを呼び出すこともできます。
# DBIx::Custom method $model->execute($sql); # DBI method $model->begin_work; $model->commit;
すべてのモデル名を取得したい場合はmodels()のキーを取得してください。
models()
my @models = keys %{$self->models};
モデルにはプライマリーキーを設定することもできます。
$model->primary_key(['id', 'number_id']);
ここで設定したプライマリーキーはinsert_at, update_at(), delete_at(), select_at()で利用されます。
filterでapply_filter()で適用されるフィルタを定義しておくこともできます。
$model->filter({ title => {out => ..., in => ..., end => ...}, author => {out => ..., in => ..., end => ...} });
このフィルタはinclude_model()を呼び出したときに自動的に適用されます。
モデルには列名を設定することもできます。
$model->columns(['id', 'number_id']);
列名はsetup_model()で自動的に設定することができます。 このメソッドはinclude_model()の後で呼び出してください。
setup_model()
$dbi->setup_model;
モデルにはjoinを設定することもできます。
$model->join(['left outer join company on book.company_id = company.id']);
ここで設定したjoinはselect(), select_at()で利用されます。
クラス名とモデル名とテーブル名の関係について書いておきます。 通常はクラス名がモデル名に利用され、テーブル名にはモデル名が利用されます。
クラス名 モデル名 テーブル名 book (クラス名) -> book (モデル名) -> book
モデル名を変更することもできます。
package MyModel::book; use base 'MyModel'; __PACAKGE__->attr(name => 'book_model'); クラス名 モデル名 テーブル名 book book_model (モデル名) -> book_model
モデル名というのは、DBIx::Customのmodel()で利用される名前です。
$dbi->model('book_model');
テーブル名を変更することもできます。
package MyModel::book; use base 'MyModel'; __PACAKGE__->attr(table => 'book_table'); クラス名 モデル名 テーブル名 book (クラス名) -> book book_table
テーブル名というのは、実際にアクセスされるテーブルです。
$dbi->model('book')->insert(...); # book_tableにアクセス
列名の節を自動生成するにはcolumn_clause()を使用します。 tableとcolumnsの値が利用されます。
column_clause()
columns
my $column_clause = $model->column_clause;
tableの値が'book'、columnの値が['id', 'name']で あった場合は次のような列名の節が生成されます。
book.id as id, book.name as name
このように列名の節を生成するのは、列名のあいまいさをなくすためです。
不必要な列がある場合はremoveオプションで指定することができます。
remove
my $column_clause = $model->column_clause(remove => ['id']);
追加したい列がある場合は、addオプションで追加することができます。
add
my $column_clause = $model->column_clause(add => ['company.id as company__id']);
モデルのサンプルです。
package MyDBI; use base 'DBIx::Custom'; sub connect { my $self = shift->SUPER::connect(@_); $self->include_model( MyModel => [ 'book', 'company' ] ); } package MyModel::book; use base 'DBIx::Custom::Model'; __PACKAGE__->attr('primary_key' => sub { ['id'] }; sub insert { ... } sub list { ... } package MyModel::company; use base 'DBIx::Custom::Model'; __PACKAGE__->attr('primary_key' => sub { ['id'] }; sub insert { ... } sub list { ... }
パフォーマンスが得られない場合はqueryオプションを使って クエリを作成してみてください。
query
my $params = [ {title => 'Perl', author => 'Ken'}, {title => 'Good day', author => 'Tom'} ] my $query = $dbi->insert(table => 'book', param => $params->[0], query => 1);
戻り値はDBIx::Custom::Queryオブジェクトです。 作成したクエリはexecute()で実行することができます。
foreach my $param (@$params) { $dbi->execute($query, $param); }
ステートメントハンドルが再利用されるので、パフォーマンスが 改善されます。 queryオプションはinsert(), update(), update_all(), delete(), delete_all()で利用することができます.
クエリを作成するメソッドに渡すパラメータと execute()に渡すパラメータの個数は同じでなければならない ことに注意してください。
create_query()を使って任意のSQLのクエリを作成 することもできます。
create_query()
my $query = $dbi->create_query( "insert into book {insert_param title author};"; );
DBIx::Customオブジェクトにメソッドを追加することができます。 method()を使用します。
method()
$dbi->method( update_or_insert => sub { my $self = shift; # something }, find_or_create => sub { my $self = shift; # something } );
これらのメソッドは DBIx::Customオブジェクトから呼び出すことができます。
$dbi->update_or_insert; $dbi->find_or_create;
結果クラスを変更することができます。 デフォルトはDBIx::Custom::Resultです。
package MyResult; use base 'DBIx::Custom::Result'; sub some_method { ... } 1; package main; use MyResult; my $dbi = DBIx::Custom->connect(...); $dbi->result_class('MyResult');
タグの解析後のSQLはパフォーマンスの理由のためにキャッシュされます。 これはchaceで設定でき、デフォルトではキャッシュを行う設定です。
chace
$dbi->cache(1);
キャッシュ方法はcache_methodで変更することができます。 デフォルトのメソッドは以下のようになっていて、 メモリ上にキャッシュが保存されます。
cache_method
$dbi->cache_method(sub { sub { my $self = shift; $self->{_cached} ||= {}; if (@_ > 1) { # キャッシュの保存 $self->{_cached}{$_[0]} = $_[1] } else { # キャッシュの取得 return $self->{_cached}{$_[0]} } } });
第一はDBIx::Customオブジェクトです。 第二引数はタグが解析される前のSQLです。 第三引数はタグの解析後のSQLの情報です。これはハッシュのリファレンスです。
第三引数が存在した場合はキャッシュを設定し、 存在しなかった場合はキャッシュを取得する実装に してください。
以下のWikiでサンプルを見ることができます。
DBIx::Custom Wiki
To install DBIx::Custom, copy and paste the appropriate command in to your terminal.
cpanm
cpanm DBIx::Custom
CPAN shell
perl -MCPAN -e shell install DBIx::Custom
For more information on module installation, please visit the detailed CPAN module installation guide.