=encoding utf8 =head1 NAME DBIx::Custom::Guide - DBIx::Custom Guide =head1 GUIDE B L is the class to make easy to execute SQL. This is L wrapper class like L or L. You can do thing more easy than L, more flexible than L. L is B is opposit of O/R mapper. The main purpose is that we respect SQL and make easy difficult works if you use only L. If you already learn SQL, it is easy to use L. I explain L a little in this section. In L, you embbed tag in SQL. select * from book where {= title} and {=author}; The part arround {} is tag. This SQL is converted to the one which contains place holder. select * from book where title = ? and author = ?; Maybe you ask me that this conversion is meaningful. On the top of this, usuful features is implemented. See the following descriptions. =over 4 =item 1. Specify place holder binding value as hash refernce If you use L, you must specify place holder binding value as array. $sth->execute(@bind); If you use L, you specify it as hash reference. my $param = {title => 'Perl', author => 'Ken'}; $dbi->execute($sql, $param); =item 2. Filtering L provides filtering system. For example, You think that about date value you want to manipulate it as date object like L in Perl, and want to convert it to database DATE format. and want to do reverse. You can use filtering system. At first, register filter. $dbi->register_filter( tp_to_date => sub { ... }, date_to_tp => sub { ... } ); next, apply this filter to each column. $dbi->apply_filter('book', 'issue_date' => {out => 'tp_to_date', in => 'date_to_tp'} ); C is perl-to-database way. C is perl-from-database way. This filter is automatically enabled in many method. $dbi->insert(table => 'book', param => {issue_date => $tp}); =item 3. Selective search condition It is difficult to create selective where clause in L. For example, If C and C<author> is specified, we create the following SQL. select * from book where title = ? and author = ?; If only C<title> is specified, the following one select * from book where title = ?; If only C<author> is specified, the following one, select * from book where author = ?; This is hard work. Generally we use modules like L<SQL::Abstract>. L<DBIx::Custom> prepare the way to make it easy. # Where object my $where = $dbi->where; # Search condition $where->clause( ['and', '{= title}', {'= author'}] ); # Setting to automatically select needed column $where->param({title => 'Perl'}); # Embbed where clause to SQL my $sql = "select * from book $where"; You can create where clause which has selected search condition. You can write nesting of where clause and C<or> condition =item 4. Methods for insert, update, delete, select L<DBIx::Custom> provides methods for insert, update, delete, select There are C<insert()>, C<update()>, C<delete()>,C<select()>. my $param = {title => 'Perl', author => 'Ken'}; $dbi->insert(table => 'book', param => $param); =item 5. Register method for table. You can register method for table. $dbi->table('book')->method( list => sub { ... }, something => sub { ... } ); use the mehtod. $dbi->table('book')->list; Many O/R mapper must create class for table, but L<DBIx::Custom> make it easy. =back L<DBIx::Custom> is very useful. See the following if you are interested in it. =head2 1. Connect to database Load L<DBIx::Custom>. use DBIx::Custom; use C<connect()> to connect to database. Return value is L<DBIx::Custom> object. my $dbi = DBIx::Custom->connect( data_source => "dbi:mysql:database=bookstore", user => 'ken', password => '!LFKD%$&', dbi_options => {mysql_enable_utf8 => 1} ); C<data_source> must be one corresponding to the database system. The following ones are data source example. B<MySQL> "dbi:mysql:database=$database" "dbi:mysql:database=$database;host=$hostname;port=$port" B<SQLite> "dbi:SQLite:dbname=$database" "dbi:SQLite:dbname=:memory:" B<PostgreSQL> "dbi:Pg:dbname=$dbname" B<Oracle> "dbi:Oracle:$dbname" "dbi:Oracle:host=$host;sid=$sid" B<ODBC(Microsoft Access)> "dbi:ODBC:driver=Microsoft Access Driver (*.mdb);dbq=hoge.mdb" B<ODBC(SQL Server)> "dbi:ODBC:driver={SQL Server};Server=(local);database=test;Trusted_Connection=yes;AutoTranslate=No;" If authentication is needed, you can specify C<user> and C<password> L<DBIx::Custom> is wrapper class of L<DBI>. You can use all methods of L<DBI> from L<DBIx::Custom> object. $dbi->do(...); $dbi->begin_work; use C<dhb()> to get database handle of L<DBI> my $dbh = $dbi->dbh; By default, the following ones is set to database handle attributes. RaiseError -> 1 PrintError -> 0 AutoCommit -> 1 If fatal error occuer, program terminate. If SQL is executed, commit is executed automatically. =head2 2. Methods for insert, update, delete, or insert There are following methods. =head3 C<insert()> use C<insert()> to insert row into database $dbi->insert(table => 'book', param => {title => 'Perl', author => 'Ken'}); C<table> is table name, C<param> is insert data. Following SQL is executed. insert into (title, author) values (?, ?); =head3 C<update()> use C<update()> to update row in database. $dbi->update(table => 'book', param => {title => 'Perl', author => 'Ken'}, where => {id => 5}); C<table> is table name, C<param> is update data, C<where> is condition. Following SQL is executed. update book set title = ?, author = ?; You can't execute C<update()> without C<where> for safety. use C<update_all()> if you want to update all rows. $dbi->update_all(table => 'book', param => {title => 'Perl', author => 'Ken'}); =head3 C<delete()> use C<delete()> to delete rows from database. $dbi->delete(table => 'book', where => {author => 'Ken'}); C<table> is table name, C<where> is condition. Following SQL is executed. delete from book where id = ?; You can't execute C<delete()> without C<where> for safety. use C<delete_all()> if you want to delete all rows. $dbi->delete_all(table => 'book'); =head3 C<select()> use C<select()> to select rows from database my $result = $dbi->select(table => 'book'); Following SQL is executed. select * from book; Return value is L<DBIx::Custom::Result> object. use C<fetch()> to fetch row. while (my $row = $result->fetch) { my $title = $row->[0]; my $author = $row->[1]; } See L<3. Fetch row/"3. Fetch row"> about L<DBIx::Custom::Result>. Continue more examples. my $result = $dbi->select( table => 'book', column => ['author', 'title'], where => {author => 'Ken'} ); C<column> is column names, C<where> is condition. Following SQL is executed. select author, title from book where author = ?; Next example. my $result = $dbi->select( table => ['book', 'rental'], where => {'book.name' => 'Perl'}, relation => {'book.id' => 'rental.book_id'} ); C<relation> is relation of tables. This is inner join. Following SQL is executed. select * from book, rental where book.name = ? and book.id = rental.book_id; Next example. my $result = $dbi->select( table => 'book', where => {author => 'Ken'}, append => 'for update', ); C<append> is string appending to end of SQL. Following SQL is executed. select * book where author = ? for update; C<appned> is also used at C<insert()>, C<update()>, C<update_all()> C<delete()>, C<delete_all()>, and C<select()>. =head3 C<execute()> SQL use C<execute()> to execute SQL $dbi->execute("select * from book;"); Process tag and execute SQL. $dbi->execute( "select * from book {= title} and {= author};" param => {title => 'Perl', author => 'Ken'} ); Following SQL is executed. select * from book title = ? and author = ?; Values of title and author is embbdeded into placeholder. See L<5. Tag/"5. Tag"> about tag. You don't have to wirte last semicolon in C<execute()>. $dbi->execute('select * from book'); =head2 3. 行のフェッチ C<select()>メソッドの戻り値はL<DBIx::Custom::Result>オブジェクトです。 L<DBIx::Custom::Result>には行をフェッチするためのさまざまなメソッドが 用意されています。 =head3 1行づつフェッチ(配列) C<fetch()> 一行フェッチして配列のリファレンスに格納するにはC<fetch()>を使用します。 while (my $row = $result->fetch) { my $title = $row->[0]; my $author = $row->[1]; } whileループを使って、すべての行を取得することができます。 =head3 最初の行だけフェッチ(配列) C<fetch_first()> 一行だけフェッチして配列のリファレンスに格納するにはC<fetch_first()> を使用します。 my $row = $result->fetch_first; 一行のフェッチが終わった後はそれ以上フェッチできなくなります。 内部的には1行のフェッチが終わった後に ステートメントハンドルのC<finish()>が実行されます。 =head3 複数行を順にフェッチ(配列) C<fetch_multi()> 複数行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはC<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'] ] =head3 すべての行をフェッチ(配列) C<fetch_all> すべての行をフェッチして配列のリファレンスを要素に持つ 配列のリファレンスに格納するにはC<fetch_all()>を使用します。 my $rows = $result->fetch_all; すべての行を格納した次のようなデータを取得できます。 [ ['Perl', 'Ken'], ['Ruby', 'Mark'] ] =head3 1行づつフェッチ(ハッシュ) C<fetch_hash()> 一行フェッチしてハッシュのリファレンスに格納するにはC<fetch_hash()>を使用します。 while (my $row = $result->fetch_hash) { my $title = $row->{title}; my $author = $row->{author}; } =head3 最初の行だけフェッチ(ハッシュ) C<fetch_hash_first()> 一行だけフェッチしてハッシュのリファレンスに格納するには C<fetch_hash_first()>を使用します。 my $row = $result->fetch_hash_first; 一行のフェッチが終わった後はそれ以上フェッチできなくなります。 内部的には1行のフェッチが終わった後に ステートメントハンドルのC<finish()>が実行されます。 =head3 複数行を順にフェッチ(ハッシュ) C<fetch_hash_multi()> 複数行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはC<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'} ] =head3 すべての行をフェッチ(ハッシュ) C<fetch_hash_all()> すべての行をフェッチしてハッシュのリファレンスを要素に持つ 配列のリファレンスに格納するにはC<fetch_hash_all()> を使用します。 my $rows = $result->fetch_hash_all; すべての行を格納した次のようなデータを取得できます。 [ {title => 'Perl', author => 'Ken'}, {title => 'Ruby', author => 'Mark'} ] =head3 ステートメントハンドル C<sth()> ステートメントハンドルに直接アクセスしたい場合は <sth>で取得することができます。 my $sth = $result->sth; フェッチのパフォーマンスが用件を満たさないときには、 ステートメントハンドルから 利用できる速度の速いメソッドを利用することができます。 =head2 4. フィルタリング データベースにデータを登録するときやデータベースからデータを取得する ときに自動的に値の変換を行いたい場合が多いと思います。 たとえば、日付を表現する列の場合は、 データベースに登録する場合はL<Time::Piece>オブジェクトから データベースの日付のフォーマットに、 データベースからデータを取得するときは、その逆を行えると便利です。 =head3 フィルタの登録 C<register_filter()> フィルタを登録するにはC<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'); }, ); 登録したフィルタはC<apply_filter()>などで利用することができます。 =head3 フィルタの適用 C<apply_filter()> 作成したフィルタを適用するには、C<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'} ); 第一引数はテーブル名です。第二引数以降は、列名とフィルタルールのペアを記述します。 フィルタルールのoutには、データベースにデータを送信するときに適用するフィルタを、 フィルタルールのinには、データベースからデータを取得するときに適用するフィルタを 記述します。outがデータベースに送信する方向、inがデータベースから取り出す方向です。 フィルタには、C<register_filter>で登録したフィルタ名の他に、コードリファレンスを 指定することもできます。 issue_date => {out => sub { ... }, in => sub { ... }} 適用されたフィルタはC<insert()>、C<update()>、C<update_all()>、C<delete()>、 C<delete_all()>、C<select()>で有効になります。 my $tp = Time::Piece->strptime('2010/10/14', '%Y/%m/%d'); my $result = $dbi->select(table => 'book', where => {issu_date => $tp}); データベースにデータが送信されるときに、L<Time::Piece>オブジェクトは データベースの日付のフォーマット「2010-10-14」に変換されます。 また逆にデータをフェッチするときには、データベースの日付のフォーマットは タイムピースオブジェクトに変換されます。 my $row = $resutl->fetch_hash_first; my $tp = $row->{issue_date}; このような自動的に実行されるフィルタを登録できることがL<DBIx::Custom>の 特徴のひとつです。 C<apply_filter()>で適用されたフィルタはテーブル名をを含む列名に対しても有効です。 $dbi->select( table => 'book', where => {'book.title' => 'Perl', 'book.author' => 'Ken'} ); テーブルを区別する必要があるときに便利な機能です。 =head3 個別のフィルタの適用 C<filter> C<apply_filter()>を使って最初にすべてのテーブルの列について フィルタを定義することもできますが、 個別にフィルタを適用することもできます。 個別のフィルタはC<apply_filter()>で適用したフィルタを上書きます。 個別のフィルタはSQLのasを使って、列の別名を作成する必要がある場合に活躍します。 データベースに送信する場合に、個別のフィルタを適用するには、各メソッドの C<filter>オプションを使用します。個別のフィルタは、C<insert()>、C<update()>、 C<update_all()>、C<delete()>、C<delete_all()>、C<select()>、C<execute()> で使用することができます。 C<insert()>の例を示します。 $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<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'} ); これはC<filter>を使う良くある例です。issue_dateの変換についてはC<apply_filter()> で登録してあるのですが、新しく作成した列であるissue_yearについては、 何の変換も登録されていません。ですので、個別にフィルタを設定しています。 また反対に行をフェッチするときにも個別のフィルタを適用することができます。 フィルタを適用するには、 C<DBIx::Custom::Result>クラスのC<filter>メソッドを使用します。 $result->filter(issue_year => 'year_to_tp'); 頻繁に利用するのであれば、個別に登録するよりもC<apply_filter()>で登録 しておいたほうが便利でしょう。C<apply_filter()>は存在しない列に対しても フィルタを適用できるからです。 $dbi->apply_filter('book', 'issue_year' => {out => 'tp_to_year', in => 'year_to_tp'} ); =head3 最終出力のためのフィルタリング C<end_filter()> C<DBIx::Custom::Result>ではさらに最後にもう一度、フィルタを追加で 登録することができます。たとえばHTMLに出力したい場合に、Time::Piece オブジェクトから読みやすい記述に変換することができます。 最後のフィルタを登録するには、C<end_filter()>を使用します。 $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<each_column()> 日付型の列は手動で設定しなくても、自動的に設定できると便利です。 このためにデータベースのテーブルの列のすべての情報を 順番に処理するためのC<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はコールバックを受け取ります。コールバックの引数は 順番にL<DBIx::Custom>オブジェクト、テーブル名、列名、列の情報です。 列の型名の情報をもとに自動的に、フィルタを適用しています。 ひとつの注意点としてコールバックの中から、コールバックの外側 の変数を参照しないように注意してください。each_columnは 高々1回だけ実行されるだけなので、ほとんどの場合問題ありませんが、 循環参照によるメモリリークが発生してしまう可能性を持っているからです。 =head2 5. タグ =head3 タグの機能 L<DBIx::Custom>はSQLの中にタグを埋め込む機能を持っています。 select * from book where {= title} and {like author}; {= title}と{like author}の部分がタグです。タグは次のような形式 を持ちます。 {タグ名 引数1 引数2 ...} タグはC<{>で始まり、C<}>で終わります。最初のC<{>とタグ名の間 には空白を挿入しないよう注意してください。 タグの機能のためにC<{>とC<}>は予約語になっています。 もし利用したい場合は直前に\をおいてエスケープを行う必要があります。 select from book \\{ ... \\} C<\>自体がPerlのエスケープ文字ですので、 エスケープする場合はC<\>が二つ必要になるという点に注意してください。 上記のタグはSQLが実行される前に次のSQLに展開されます。 select * from book where title = ? and author like ?; タグを含むSQLを実行するにはC<execute()>を使用します。 my $sql = "select * from book where {= author} and {like title};" $dbi->execute($sql, param => {title => 'Perl', author => '%Ken%'}); C<param>オプションを使って、プレースホルダに埋め込みたい値を ハッシュリファレンスで指定することができます。 他のメソッドと同様にC<execute()>においてもC<filter>を指定することができます。 $dbi->execute($sql, param => {title => 'Perl', author => '%Ken%'} filter => {title => 'to_something'); C<execute>のひとつの注意点としてはC<apply_filter()>で適用されたフィルタ はデフォルトでは有効ではないということに注意してください。 C<apply_filter()>で適用されたフィルタを有効にするには、 C<table>を指定する必要があります。 $dbi->execute($sql, param => {title => 'Perl', author => '%Ken%'} table => ['book']); =head3 タグ一覧 L<DBIx::Custom>では次のタグが使用可能です。 このようにタグを使ってSQL文を表現するのがL<DBIx::Custom>の 特徴です。以下のタグが利用可能です。 =head4 C<?> C<?>タグは以下のように展開されます。 {? NAME} -> ? =head4 C<=> C<=>タグは以下のように展開されます。 {= NAME} -> NAME = ? =head4 C<E<lt>E<gt>> C<E<lt>E<gt>>タグは以下のように展開されます。 {<> NAME} -> NAME <> ? =head4 C<E<lt>> C<E<lt>>タグは以下のように展開されます。 {< NAME} -> NAME < ? =head4 C<E<gt>> C<E<gt>>タグは以下のように展開されます。 {> NAME} -> NAME > ? =head4 C<E<gt>=> C<E<gt>=>タグは以下のように展開されます。 {>= NAME} -> NAME >= ? =head4 C<E<lt>=> C<E<lt>=>タグは以下のように展開されます。 {<= NAME} -> NAME <= ? =head4 C<like> C<like>タグは以下のように展開されます。 {like NAME} -> NAME like ? =head4 C<in> C<in>タグは以下のように展開されます。プレースホルダの 数を引数で指定する必要があることに注意してください。 {in NAME COUNT} -> NAME in [?, ?, ..] =head4 C<insert_param> C<insert_param>タグは以下のように展開されます。 {insert_param NAME1 NAME2} -> (NAME1, NAME2) values (?, ?) =head4 C<update_param> C<update_param>タグは以下のように展開されます。 {update_param NAME1 NAME2} -> set NAME1 = ?, NAME2 = ? =head3 同名の列の扱い 同名の列を含むタグがある場合にも、SQLを実行することができます。 たとえば、二つの日付で比較しなければならない場合を 考えて見ましょう。 my $sql = "select * from table where {> date} and {< date};"; このような場合は対応するパラメータの値を配列のリファレンスにします。 my $dbi->execute($sql, param => {date => ['2010-10-01', '2012-02-10']}); =head3 タグの追加 C<register_tag()> L<DBIx::Custom>ではタグを独自に追加することができます。 タグを追加するにはC<register_tag()>を使用します。 $dbi->register_tag( '=' => sub { my $column = shift; return ["$column = ?", [$column]]; } ); ここではデフォルトのC<=>タグがどのように実装されているかを示しています。 タグを登録する関数の引数はタグの中に書かれた引数になります。 {タグ名 引数1 引数2} C<=>タグの場合は {= title} という形式ですから、サブルーチンにはtitleというひとつの列名がわたってきます。 サブルーチンの戻り値には次の形式の配列のリファレンスを返す必要があります。 [ 展開後の文字列, [プレースホルダに埋め込みに利用する列名1, 列名2, ...] ] 一つ目の要素は展開後の文字列です。この例では 'title = ?' を返す必要があります。 二つ目の要素はプレースホルダに埋め込みに利用する列名を含む配列の リファレンスです。今回の例では ['title'] を返す必要があります。複数のプレースホルダを含む場合は、この部分が 複数になります。C<insert_param>タグやC<update_param>タグは この部分が実際複数になっています。 上記を合わせると ['title = ?', ['title']] を返す必要があるということです。 タグの実装の他のサンプルはL<DBIx::Custom::Tag>のソースコード をご覧になってみてください。 =head2 6. Where句の動的な生成 =head3 Where句の動的な生成 where() 複数の検索条件を指定して、検索を行いたい場合があります。 次の3つのケースのwhere句を考えてみましょう。 下記のようなwhere句が必要になります。 titleの値だけで検索したい場合 where {= title} authorの値だけで検索したい場合 where {= author} titleとauthorの両方の値で検索したい場合 where {= title} and {=author} L<DBIx::Custom>では動的なWhere句の生成をサポートしています。 まずC<where()>でL<DBIx::Custom::Where>オブジェクトを生成します。 my $where = $dbi->where; 次にC<clause()>を使用してwhere句を記述します。 $where->clause( ['and', '{= title'}, '{= author}'] ); clauseの指定方法は次のようになります。 ['or' あるいは 'and', タグ1, タグ2, タグ3] 第一引数にはorあるいはandを指定します。第二引数以降には 検索条件をタグを使って記述します。 C<clause>の指定は入れ子にすることもでき、さらに複雑な条件 を記述することもできます。 ['and', '{= title}', ['or', '{= author}', '{like date}'] ] このようにC<clause>を設定した後にC<param>にパラメータを指定します。 my $param => {title => 'Perl'}; $where->param($param); この例ではtitleだけがパラメータに含まれています。 この後C<to_string()>を実行すると$paramに含まれるパラメータを満たす where句を生成することができます。 my $where_clause = $where->to_string; パラメータはtitleだけですので、次のようなwhere句が生成されます。 where {= title} またL<DBIx::Custom>は文字列の評価をオーバーロードして、C<to_string()> を呼び出すようにしていますので、次のようにしてwhere句を生成することも できます。 my $where_clause = "$where"; これはSQLの中にwhere句を埋め込むときにとても役立つ機能です。 =head3 同一の列名を含む場合 タグの中に同一の名前を持つものが存在した場合でも動的に 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']}; L<DBIx::Custom>のC<not_exists>でDBIx::Custom::NotExistsオブジェクトを 取得できます。これは対応する値が存在しないことを示すためのものです。 また終了日付が存在しない場合は次のようなデータを作成します。 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}; =head3 C<select()>との連携 L<DBIx::Custom::Where>オブジェクトは C<select()>のC<where>に直接渡すことが できます。 my $where = $dbi->where; $where->clause(...); $where->param($param); my $result = $dbi->select(table => 'book', where => $where); あるいはC<update()>、C<delete()>のwhereに指定することも可能です。 =head3 C<execute()>との連携 C<execute()>との連携です。SQLを作成するときに埋め込むことができます。 my $where = $dbi->where; $where->clause(...); $where->param($param); my $sql = <<"EOS" select * from book; $where EOS $dbi->execute($sql, param => $param); =head2 7. テーブルモデル =head3 テーブルオブジェクトの作成 C<table()> L<DBIx::Custom>はロジックとしてテーブルを中心にすえた モデルの作成を支援します。 アプリケーションのロジックを記述するときに、そのロジックを データベースのテーブルにすえることは、RDBMSを利用する モデルであれば、コードの重複も少なく わかりやすいものになります。 テーブルオブジェクトを生成するにはC<table()>を使用します。 my $table = $dbi->table('book'); 実際にデータベースにテーブルは存在している必要はありません。 これは仮想的なテーブルオブジェクトです。これは L<DBIx::Customm::Table>オブジェクトになります。 テーブルオブジェクトからはC<insert()>、C<update()>、C<update_all()>、 C<delete()>、C<delete_all()>、C<select()>などのメソッド呼び出すことができます。 L<DBIx::Custom>と異なるところは、C<table>を必ずしも指定する必要が ないということです。 $table->insert(param => $param); C<table>の値は自動的にbookに設定されます。 またテーブルオブジェクトには独自のメソッドを追加することができます。 $table->method( register => sub { my $self = shift; my $table_name = $self->name; # something }, list => sub { my $self = shift; my $table_name = $self->name; # something } ); メソッドに渡される第一引数はL<DBIx::Custom::Table>オブジェクトです。 C<name()>を使用して、テーブル名を取得することができます。 このようにして登録したメソッドは直接呼び出すことができます。 $table->register(...); $table->list(...); またテーブル専用のメソッドをオーバーライドして作成することもできます。 $table->method( insert => sub { my $self = shift; $self->base_insert(...); # something } ); もともと存在していたC<insert()>を呼ぶにはC<base_$method>とします。L<DBIx::Custom::Table> のオーバーライドの機能は簡易的なものですが、とても便利です。 =head2 テーブルで共有のメソッドの登録 すべてのテーブルでメソッドを共有するにはC<table>メソッドでテーブルを作成する前に、 C<base_table>にメソッドを登録しておきます。 $dbi->base_table->method( count => sub { my $self = shift; return $self->select(column => ['count(*)']); } ); またテーブルからはL<DBIx::Custom>とL<DBI>のすべてのメソッドを呼び出すことができます。 # DBIx::Custom method $table->execute($sql); # DBI method $table->begin_work; $table->commit; =head2 一般的なモデルの構成 一般的には、L<DBIx::Custom>を継承してコンストラクタの中に、モデルを作成 するのがよいでしょう。 package MyDBI; use base 'DBIx::Custom'; sub connect { my $self = shift->SUPER::connect(@_); $self->base_table->method( ... => sub { ... } ); $self->table('book')->method( insert_multi => sub { ... }, ... => sub { ... } ); $self->table('company')->method( ... => sub { ... }, ); } このようにして定義しておけば、次のように利用することができます。 my $dbi = MyDBI->connect(...); $dbi->table('book')->insert_multi(...); =head2 8. パフォーマンスの改善 =head3 クエリの作成 もしC<insert()>メソッドを使用してインサートを実行した場合、 必要なパフォーマンスを得られない場合があるかもしれません。 C<insert()>メソッドは、SQL文とステートメントハンドルを 毎回作成するためです。 そのような場合は、C<query>オプションを指定することで、 クエリを取得することができます。 my $query = $dbi->insert(table => 'book', param => $param, query => 1); またC<create_query()>メソッドを使って任意のSQLのクエリを作成 することもできます。 my $query = $dbi->create_query( "insert into book {insert_param title author};"; ); 戻り値はL<DBIx::Custom::Query>オブジェクトです。 このオブジェクトはSQL文とパラメータバインド時の列名を 保持しています。またステートメントハンドルも保持しています。 { sql => 'insert into book (title, author) values (?, ?);', columns => ['title', 'author'], sth => $sth } クエリオブジェクトを使って繰り返し実行するにはC<execute()>を使用します。 my $params = [ {title => 'Perl', author => 'Ken'}, {title => 'Good days', author => 'Mike'} ]; foreach my $param (@$paramss) { $dbi->execute($query, table => 'book', param => $input); } C<execute>メソッドの第一引数にクエリオブジェトを渡すことができます。 C<insert()>メソッドよりも高速です。 注意点がいくつかあります。それはパラメータの数は必ず同じでなくてはならない ということです。最初に3つのパラメータだけを渡したのに、次の実行では 二つのパラメータを渡すと予期しない結果になります。それは 動的に生成されたSQLに含まれるプレースホルダの数が異なるからです。 またC<execute()>によっては自動的にはフィルタが有効にならないので、 C<table>を指定する必要のあることに注意してください。 本当に必要な場合だけ利用してください。 =head2 9. その他の機能 =head3 メソッドの登録 メソッドを登録するにはC<method()>を使用します。 $dbi->method( update_or_insert => sub { my $self = shift; # something }, find_or_create => sub { my $self = shift; # something } ); <method()>で登録したメソッドは L<DBIx::Custom>オブジェクトから直接呼び出すことができます。 $dbi->update_or_insert; $dbi->find_or_create; =head3 結果クラスの変更 必要ならば結果クラスを変更することができます。 package MyResult; use base 'DBIx::Custom::Result'; sub some_method { ... } 1; package main; use MyResult; my $dbi = DBIx::Custom->connect(...); $dbi->result_class('MyResult'); =head3 キャッシング タグの展開後のSQLはパフォーマンスの理由のためにキャッシュされます。 これはC<chace>で設定でき、デフォルトではキャッシュを行う設定です。 $dbi->cache(1); キャッシュ方法はC<cache_method>にメソッドを指定することで 変更することができます。 データの保存と取得のためのメソッドを定義します。 デフォルトでは次のようにメモリ上にキャッシュを行うものになっています。 $dbi->cache_method(sub { sub { my $self = shift; $self->{_cached} ||= {}; if (@_ > 1) { # Set $self->{_cached}{$_[0]} = $_[1] } else { # Get return $self->{_cached}{$_[0]} } } }); 第一はL<DBIx::Custom>オブジェクトです。 第二引数はタグの展開される前のSQLです。 第三引数はタグの展開後のSQLです。 自分で作成する場合は第三引数が存在した場合はキャッシュを設定し、 存在しなかった場合はキャッシュを取得する実装に してください。 =cut =head1 EXAMPLES L<DBIx::Custom Wiki|https://github.com/yuki-kimoto/DBIx-Custom/wiki> - Many useful examples =cut