Создание подкласса Perl DBI, перезапись обработчика базы данных в вызывающем скрипте при отключении

#perl #dbi

#perl #dbi

Вопрос:

У меня такая проблема:

псевдокод:

 package a;

@ISA = qw(DBI);

package a::db;
@ISA = qw(DBI::db);
    sub prepare{
      #gets a $dbh object, which we can overwrite upon disconnection
      #so that the calling script gets a fresh and working dbh, 
      #through $_[0] = $newdbh;
    }

package a::st;
@ISA = qw(DBI::st);

sub execute{
  #gets a $sth object which we can rescue upon disconnection 
  #before the execute, but the calling script will have a faulty $dbh
}
  

вызывающий скрипт:

 use a;
my $dbh = a->connect;
my $sth = $dbh->prepare("select 1");
$dbh->disconnect; #I detect this in the execute function
$sth->execute; #disconnect detected, reconnect -> reprepare and execute again 
               #(is done in the module a) = we're up and running!
  

Есть ли какой-либо способ повлиять на объект $ dbh без повторного вызова $dbh->prepare в вызывающем скрипте?

Моя проблема также в том, что я хочу сделать это без проблем, потому что я хочу исправить обработку базы данных во многих проектах, просто используя мой новый модуль базы данных.

Возможно ли это вообще?

Или, может быть, я мог бы также создать обработчик инструкций из::prepare части пакета ::db? Что, в свою очередь, будет означать, что обработчик базы данных из a::connect также должен быть обработчиком инструкций, чтобы при использовании connect я получал объект с доступом как к подготовке, так и к выполнению. Тогда я мог бы изменить $ _ [0] в a::execute на новый объект dbh, что означало бы, что после повторного подключения в a::execute вызывающий скрипт будет иметь действительный $ dbh. Перепробовал много способов сделать это, но я подозреваю, что внутренняя магия DBI усложняет задачу…

Комментарии:

1. Что вы подразумеваете под «повлиять на объект $ dbh»? Можете ли вы быть более конкретным? Вы просто хотите повторно использовать все еще действующий / открытый дескриптор базы данных или вы повторно подключаетесь? Может DBI->connect_cached быть, это то, что вы хотите, или, может быть, что-то вроде наворотов DBIx::Class ?

2. Я имею в виду, что в следующий раз, когда я использую обработчик $dbh / database в скрипте после повторного подключения, он не будет действительным в приведенном выше случае, потому что у меня нет способа «перенести» действительный обработчик базы данных, созданный в подпрограмме выполнения в a::st, в сценарий с использованием функции.

3. Только после использования prepare вызывающий скрипт получит действительный обработчик базы данных.. Я подумал, может быть, у perl есть способ волшебным образом установить $ dbh вызывающего скрипта … в противном случае мне придется переосмыслить и, возможно, попытаться сделать оба :: st и :: db частью одного и того же пакета / объекта, но DBI, похоже, имеет базовую защиту от таких вещей…

4. Для экземпляров вашего подкласса DBI::st обнаружить, что их DBI::db отключен, а затем повторно подключиться и запросить все достаточно просто. В конце концов, DBI ::st знает, с каким дескриптором базы данных он связан ( $sth->{Database} ), знает SQL-запрос ( $sth->{Statement} ) и знает о любых привязках ( $sth->{ParamValues} ). Однако, если ваша база данных отключила вас на полпути при циклическом просмотре результирующего набора из 1000 записей, повторное подключение и повторный запрос к базе данных вернут вас к началу результирующего набора. (Который на этот раз может быть возвращен в другом порядке!)

5. … Таким образом, лучшим решением является исправление того, что заставляет ваш сервер базы данных отключать вас в первую очередь. Слишком высокая нагрузка? Ненадежная сеть? Смехотворно низкое время ожидания для запросов?

Ответ №1:

Подклассификация — это способ создания класса, который ведет себя идентично другому классу, но с некоторыми уточнениями.

Ваш a класс (не могли бы вы придумать худший идентификатор?) похоже, это не похоже DBI , и я не удивлен, что у вас возникли трудности с выражением того, что вы хотите в этих терминах. Похоже, вы хотите a , чтобы объект имел DBI соединение, а не был им.

Так что, возможно, вы могли бы написать

 package a;

use strict;
use warnings;

use DBI;

sub connect {
  my $self = {};
  $self->{dbh} = DBI->connect(@_);
  $self;
}
  

Куда вы идете отсюда, я не уверен, поскольку вы описали предлагаемое решение, а не саму проблему

Комментарии:

1. Да, я делаю подклассификацию, чтобы я мог просто загрузить модуль, и все будет работать как обычно, только мой новый модуль будет переподключаться и повторять попытки (взаимоблокировки / innodb), и я также написал исключения для случаев, когда таблицы заблокированы или автоматическая фиксация отключена.

2. Принимая это, поскольку это единственное, что, кажется, имеет смысл, просто делайте это правильно, вместо того, чтобы надеяться, что я могу каким-то волшебным образом связать или повлиять на обработчик базы данных, который принадлежит скрипту, через мой подкласс..