DBIx::Classその1

DBIx-Class-0.04のトレースです。
間違ってるとこあると思うので、あんまり信用しないで下さい。
合間合間にやったので(言い訳)^^;

まず、findメソッドで処理を追ってみる。

今回テストするテーブル構造ととりあえずのデータは以下


mysql> desc order_cust;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| no | int(10) | | PRI | NULL | auto_increment |
| name | varchar(255) | | | | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
mysql> select * from order_cust;
+----+----------+
| no | name |
+----+----------+
| 1 | nekokak |
| 2 | nomaneko |
| 3 | nekokak |
+----+----------+
3 rows in set (0.03 sec)
mysql

DBIx::Classの継承モジュール


package TestDB;
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__->load_components(qw/Core DB/);
__PACKAGE__->connection('dbi:mysql:nekodb','nekokak','******');
1;

order_custテーブルマッピングモジュール


package OrderCust;
use strict;
use warnings;
use base 'TestDB';
__PACKAGE__->table('order_cust');
__PACKAGE__->add_columns(qw/no name/);
__PACKAGE__->set_primary_key('no');
1;

実行スクリプト


#! /usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;
use OrderCust;
$\ = "\n";
my $s = OrderCust->find(1);
print Dumper($s);

とりあえずこんなので。

実行スクリプトで、OrderCustをuseした時点で、TestDBのload_componentsメソッドとconnectionメソッドが実行される。
load_componentsメソッドによってDBIx::Class::CoreとDBIx::Class::DBがuseされる。
load_componentsメソッド内部では、CoreとDBがDBIx::Class::CoreとDBIx::Class::DBという形に整形されて
_load_componentsメソッドを呼び出します。_load_componentsメソッドではevalを使ったuseでDBIx::Class::CoreとDBIx::Class::DBをロードします。
その後に、inject_baseメソッドを呼び出しています。
inject_baseメソッドではこの場合TestDBのISAにDBIx::Class::CoreとDBIx::Class::DBを追加させています。
で、Class::C3::_dump_MRO_tableで取れるテーブルにTestDBが存在しない場合、package $target; import Class::C3;をeval実行させています。
importを自分でつかったことないなぁ。
次にconnectionメソッドでDBへのコネクションが実行されると。
DBIx::Class::DBのconnectionメソッドでは、
DBIx::Class::Strage::DBIのnewメソッドが実行される。
環境変数$ENV{DBIX_CLASS_STORAGE_DBI_DEBUG}が設定されていれば、Debugフラグを立ててる。
で、DBIx::Class::Strage::DBIのconnect_infoフィールドにDBへの接続情報を保存、
作成した、DBIx::Class::Strage::DBIのオブジェクトを
DBIx::Class::DBのstorageフィールドに保存して終了。
commitしたりする場合は、DBIx::Class::DBのdbi_commitメソッドを実行すれば、
DBIx::Class::Strage::DBIのcommitメソッドが実行され、$_[0]->dbh->commitと実行される。

次に、OrderCustモジュール内の、
tableメソッドが引数order_custとともに実行される。
tableメソッドはDBIx::Class::Tableのメソッドで、
_table_nameというフィールドに引数で渡されたorder_custが設定される。
次にadd_columnsメソッドにnoとnameという引数とともに実行される。
add_columnsメソッドもDBIx::Class::Tableのメソッド。
add_columnsメソッドではまず、_register_columnsメソッドが上記で設定した
引数とともに実行される。
_register_columnsメソッドでは、_columnsフィールドに引数で渡された、
noとnameが設定される。
_register_columnsメソッドの中身だけど、


my $names = { %{$class->_columns} };
$names->{$_} ||= {} for @cols;
$class->_columns($names);

こんなの。


my $names = { %{$class->_columns} };

これで、ハッシュリファレンスを取って、


$names->{$_} ||= {} for @cols;

これで、引数を設定と。
ややこしいなぁ。

add_columnsメソッドでは次に_mk_column_accessorsメソッドを実行、
_mk_column_accessorsメソッドではmk_group_accessorsを使って、カラムへのアクセサを設定と。

次にOrderCustでset_primary_keyメソッドを実行。
set_primary_keyメソッドはDBIx::Class::PKのメソッド。
set_primary_keyメソッドではTie::IxHash使って_primariesフィールドにtieしたハッシュを設定。

で、やっとfindメソッドです!!
findメソッドはDBIx::Class::PKのメソッドですね。
findメソッドの2行目の


my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});

は、現在使われておりません。
findメソッドでは_primariesフィールドにあるプライマリーカラムを取得します。
そしてfindメソッドの引数で渡したパラメータを編集して、$queryを作成します。
$queryで作成した数とプライマリカラムの数が一致しなければエラーです。
次に、$class->_select_columnsでカラムリストを取得、
DBIx::Class::Storage::DBIのselect_singleメソッドを実行
select_singleメソッドではDBIx::Class::Storage::DBIの_selectメソッドを実行。
_selectメソッドではDBIx::Class::Storage::DBIの_executeメソッドを実行。
_executeメソッドでは、DBIのexecuteメソッドを実行してます。

で、findメソッドを実行した結果のダンプが↓


$VAR1 = bless( {
'_in_storage' => 1,
'_column_data' => {
'name' => 'nekokak',
'no' => '1'
}
}, 'OrderCust' );

findメソッドのパラメータを2に変えて実行した結果のダンプ↓


$VAR1 = bless( {
'_in_storage' => 1,
'_column_data' => {
'name' => 'nomaneko',
'no' => '2'
}
}, 'OrderCust' );

こんな感じでした。

ってかあってるのか。。。
机上だからなぁ。。。

(追記)
searchの場合、katoさんも書いてらっしゃいますが、
http://www.lost-season.jp/mt/2005/11/2005112322.html
searchメソッドを実行してもその時点ではSQLは実行されず、


searchメソッド実行

nextメソッドの実行
 ↓       ↓
 初回の場合   2回目以降の場合
 ↓       ↓
 executeを実行  ↓
 ↓       ↓
 fetchrow_arrayを実行

こんな感じになります。