Rose::DB в целом хороший фреймворк, но есть некоторые неприятности…
В Rose::DB используется хитрый бестолковый бессмысленный идиотский механизм подключения к базе. Чем это вызвано непонятно. Мне хочется иметь подключение такое же, как обычно в скриптах, использующих DBI, то есть что-то типа
my $dbh = DBI->connect( $c->{dsn}, $c->{user}, $c->{password}, $c->{opt} );
<инициализация Rose с указанием нужного $dbh>
<использование всех прелестей Rose>
Вместо этого надо создать конфигурацию для типа базы и домена, и наслаждаться постоянными подключениями к базе с сопутствующими потерями производительности. С этим можно было бы мириться, так как $dbh из Rose получить можно, но проблемы начинаются при восстановлении подключения при обрыве связи с базой — встроенного механизма в Rose нет. Выход — как-то заставить Rose использовать не свой Rose::DB и $dbh, а то что мы ему дадим. Механизмы для этого есть — это организация надстройки (потомка) над Rose::DB и Rose::DB::Object и перехват функций dbi_connect, init_dbh, init_db. Управление соединением, таким образом, выносится за пределы Rose и может быть как угодно восстанавливаться и проверяться на обрыв. Единственная засада — В Rose::DB правитcя стандартный $dbh , добавляя в него кучу лишних опций. Непонятно пока что такое $self->{‘__dbh_attributes’} и откуда оно берётся. По идее это атрибуты $dbh;
Rose надо уведомить, что не он управляет соединением с базой. Для этого надо установить атрибут
_dbh_has_foreign_owner:
$db — объект класса Rose::DB.
$db->{_dbh_has_foreign_owner}=1;
Другой способ — переопределить процедуру Rose::DB::release_dbh:
package ORM::DB;
use strict;
use base qw(Rose::DB);
__PACKAGE__->use_private_registry;
__PACKAGE__->register_db(
driver => 'Pg',
);
sub release_dbh {
return 1;
}
1;
Особый интерес представляют атрибутs private_pid и private_pid. Они устанавливаются в init_dbh(), так что при написании своего init_dbh() их надо не забыть установить. Если этого не сделать, то будет забавная проблема — в morbo всё работает, а в hypnotoad нет (это Mojolicious).
# Rose::DB проверяет эти переменные. И установить их надо именно в текущем процессе. $dbh_dc->{'private_pid'} = $$; $dbh_dc->{'private_tid'} = threads->tid if ($INC{'threads.pm'});
sub init_dbh { my($self) = shift; my $options = $self->connect_options; $options->{'private_pid'} = $$; $options->{'private_tid'} = threads->tid if($INC{'threads.pm'}); my $dsn = $self->dsn; $self->{'error'} = undef; $self->{'database_version'} = undef; $self->{'_dbh_refcount'} = 0; $self->{'_dbh_has_foreign_owner'} = undef; my $dbh = $self->dbi_connect($dsn, $self->username, $self->password, $options); unless($dbh) { $self->error("Could not connect to database: $DBI::errstr"); return undef; } if($dbh->{'private_rose_db_inited'}) { # Someone else owns this dbh $self->{'_dbh_has_foreign_owner'} = 1; } else # Only initialize if this is really a new connection { $dbh->{'private_rose_db_inited'} = 1; if($self->{'__dbh_attributes'}) { foreach my $attr (keys %{$self->{'__dbh_attributes'}}) { my $val = $self->dbh_attribute($attr); next unless(defined $val); $dbh->{$attr} = $val; } } if((my $sqls = $self->post_connect_sql) && !$dbh->{DID_PCSQL_KEY()}) { my $error; TRY: { local $@; eval { foreach my $sql (@$sqls) { #$Debug && warn "$dbh DO: $sql\n"; $dbh->do($sql) or die "$sql - " . $dbh->errstr; } }; $error = $@; } if($error) { $self->error("Could not do post-connect SQL: $error"); $dbh->disconnect; return undef; } $dbh->{DID_PCSQL_KEY()} = 1; } } $self->{'_dbh_refcount'} = 1; return $self->{'dbh'} = $dbh; }
По хорошему надо полностью продублировать дополнительный функционал, но обязательно надо добавить опции ‘private_pid’ и ‘private_tid’ — они будут потом постоянно проверяться. Вообще создаётся впечатление, что половину кода из Rose::DB можно смело убрать, так как на поддержание всяких странных фич тратится куча кода. И всё равно остаётся открытым вопрос — как использовать одну модель с разными подключениями, например с разными именами пользователей и с разными схемами. Стандартный способ — в каждый запрос типа ORM::Table или ORM::Table::Manager передавать свой $db — криво. Хорошо было бы создавать объект Rose и для него вызывать функции — было бы гораздо проще. Так как есть сейчас удобно для очень простых применений, и есть подозрение что создавалось в рассчёте на Mysql.
Leave a Reply
You must be logged in to post a comment.