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

时间:2017-03-22 01:05:42

标签: perl dbi

我需要使用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之前完成),我需要做什么。我应该对外部程序进行系统调用以等待它完成吗?我的问题是否有意义?

3 个答案:

答案 0 :(得分:3)

您描述的方法运行正常。

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

你没有提到的其他事情导致问题。

答案 1 :(得分:0)

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

感谢你们所有的帮助,伙计们 - 学到了很多。

答案 2 :(得分:-1)

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

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

您可以通过调用带有dbi_connect_method属性的连接来告诉DBI 这样做:

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超出范围时,如果没有该数据库句柄的其他副本,它将被关闭;明确要求断开连接是不必要的。