研究でDBをいじる機会を得たので、せっかくだからperlのO/RマッパであるDBIx::Classを使ってみることにした。そして例によってハマったのでメモっておく。
各テーブルに対応したクラスをDBIx::Class::Schema::Loaderで自動生成して(マニュアルのワンライナー参照)
一緒に掃き出されたSchemaクラスに以下の一行を追加した。
__PACKAGE__->connection('dbi:Pg:dbname=ga;host=localhost', 'postgres', '', { AutoCommit => 0 });
optionでAutoCommit => 0としたのは複数のテーブルをupdateする際に手動でコミットしたかったから。多分そっちの方がエラーの処理が書きやすいかなと思った。後の地雷を作ることになる。
find等のメソッドが動くことを確認したので次に複数のテーブルにまたがるinsert処理を書いた。
抜粋するとこんな感じmy $schema = GA::Schema->connect; $schema->txn_begin; (ここに一連のinsertやupdate処理) $schema->txn_commit;
だが、実行してもDBのテーブルは変化せず。
$schema->storage->debug(1);
とやってデバッグメッセージでSQLをはき出してみたが、ちゃんと発行されてる。どうやらうまくコミットされてないようだ。
とりあえずDBIx::Class::Schemaのマニュアルを見てみる。
Equivalent to calling $schema->storage->txn_begin. See "txn_begin" in DBIx::Class::Storage::DBI for more information.
てなワケでDBIx::Class::Storage::DBIの該当箇所のソースを読んでみる。
http://search.cpan.org/src/ASH/DBIx-Class-0.08006/lib/DBIx/Class/Storage/DBI.pmより
sub txn_begin { my $self = shift; $self->ensure_connected(); if($self->{transaction_depth} == 0) { $self->debugobj->txn_begin() if $self->debug; # this isn't ->_dbh-> because # we should reconnect on begin_work # for AutoCommit users $self->dbh->begin_work; } $self->{transaction_depth}++; }
sub txn_commit { my $self = shift; if ($self->{transaction_depth} == 1) { my $dbh = $self->_dbh; $self->debugobj->txn_commit() if ($self->debug); $dbh->commit; $self->{transaction_depth} = 0 if $self->_dbh_autocommit; } elsif($self->{transaction_depth} > 1) { $self->{transaction_depth}-- } }
マニュアルではtxn_begin : Begins a transaction (does nothing if AutoCommit is off) っていってたけど嘘じゃん!どうやらtransaction_depthがコミットするか否かの鍵を握っているようだ。早速この値がどう変化しているのか調べてみる。
my $schema = GA::Schema->connect; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #1 $schema->txn_begin; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #2 (ここに一連のinsertやupdate処理) $schema->txn_commit; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #1
AutoCommit => 0 のときはtxn_beginをやらなくても常に一段余計に重ねた部分にいるようだ。2の状態でtxn_commitしても発動しないので$schema->txn_begin;を消したら行けた。ただ、ちょっと綺麗じゃない気がしたのでAutoCommit => 1に変えてやってみた。
(AutoCommit => 1指定時) my $schema = GA::Schema->connect; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #0 $schema->txn_begin; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #1 (ここに一連のinsertやupdate処理) $schema->txn_commit; print Data::Dumper::Dumper($schema->storage->{transaction_depth}); #0
これでもDBに登録できるのを確認した。こっちの方が綺麗だし
DBIx::Class::Storage::DBIのマニュアルにも
If you set AutoCommit => 0 in your connect info, then you are always in an assumed transaction between commits, and you're telling us you'd like to manage that manually. A lot of DBIC's magic protections go away. We can't protect you from exceptions due to database disconnects because we don't know anything about how to restart your transactions. You're on your own for handling all sorts of exceptional cases if you choose the AutoCommit => 0 path, just as you would be with raw DBI.
ってあるし、AutoCommit => 0は避けて、手動コミットしたくなったらSchemaクラスtxn_beginでトランザクションを開始した方が無難なのかねぇ。