有没有办法在 Perl 中本地化包 - 数据库处理问题?

Is there a way to localise a package in Perl - database handle issues?

我需要使用 DBI 实例化一个新的 $dbh。

我的对象在创建时通常有一个 $dbh。

当我尝试使用

创建一个新的 $dbh 时
my $dbh = MyLib::Connect();

并在执行一些数据库操作后

$dbh->disconnect();

我的下游代码的 $dbh 已关闭。有没有办法得到我想要的东西?我看过一些示例代码执行两个 DBI->connect(...) 调用,但使用相同的代码作为示例会产生相同的结果——就像 MyLib 正在缓存返回的 $dbh 值。

示例代码:

package MyLib;
sub DoConnect { 
  ...
  my $dbh = DBI->connect(...);
  return($dbh)
}

package Object;
sub GetData {
  my ($id) = @_;
  my $dbh = MyLib::DoConnect(); # This should be separate
  ...
  $dbh->commit()
  $dbh->disconnect();
  return($someData);
}

package AnotherObject;
sub DoSomething {
  my ($self) = @_; 
  # $self had a dbh set on instantiation with MyLib::DoConnect();
  my $newData = Object::GetData($self->id);
  my $moreData = GetDataUsingDBH($self->dbh); # the dbh is closed!!!
}

我是否需要在不启动单独线程的情况下做可能的事情(我不能保证它会在调用 GetDataUsingDBH 之前完成)。我应该对外部程序进行系统调用以等待它完成吗?我的问题有道理吗?

您确定 DoConnect 正在调用 DBI->connect,而不是 DBI->connect_cached,或者其他正在缓存数据库句柄的东西吗?

如果是这样,我猜你已经加载了 Apache::DBI 并且 运行 在 mod_perl 下(或者至少设置了 $ENV{MOD_PERL}),这会导致 DBI ->connect 以使用 Apache::DBI 缓存连接。

您可以告诉 DBI 不要 通过使用 dbi_connect_method 属性调用连接来做到这一点:

DBI->connect( $data_source, $username, $auth, {
    'dbi_connect_method' => 'connect',
} );

(这将要求您将某些内容传递到您的 DoConnect 中,说明这就是您想要的)。

这是记录的方式;一种可能对您有用的未记录的方法是在本地设置 DBI 使用的变量作为该属性的默认值:

# This should be separate
my $dbh = do { local $DBI::connect_via = 'connect'; MyLib::DoConnect() };

(我想即使您在加载 DBI 时没有设置 $ENV{MOD_PERL} 也可能是您的代码已经将 $DBI::connect_via 设置为 'connect_cached' 或一些其他包,这给你带来了麻烦。)

另一种方法,如果您实际上不需要同时使用两个数据库句柄,则只需删除断开连接调用。当 $dbh 超出范围时,如果周围没有该数据库句柄的其他副本,它将被关闭;没有必要显式调用断开连接。

您描述的方法工作正常。

package MyLib;

use DBI qw( );

sub DoConnect {
   return DBI->connect(
      'dbi:SQLite:foo.sqlite3', undef, undef,
      { PrintError=>0, RaiseError=>1 },
   );
}


package Object;

sub new {
   my $class = shift;
   my $self = bless({}, $class);
   return $self;
}

sub GetData {
   my $dbh = MyLib::DoConnect();
   $dbh->disconnect();
}


package AnotherObject;

sub new {
   my $class = shift;
   my $self = bless({}, $class);
   $self->{dbh} = MyLib::DoConnect();
   return $self;
}

sub DoSomething {
   my ($self) = @_;
   return $self->{dbh}->selectrow_array("SELECT 'abc'");
}


package main;

my $ao = AnotherObject->new();
my $o = Object->new();
$o->GetData();
print $ao->DoSomething(), "\n";

输出:

abc

您没有提及的其他原因导致了问题。

我明白了。我将我的 DBH 设置为由可执行文件中的所有对象共享,因此我不必实例化一个 DBH 并将其手动传递给我的对象——这破坏了我实际需要执行此操作时预期的行为。

感谢大家的帮助 - 学到了很多东西。