2017-02-21 18 views
2

我处理的模块提供了自动重新连接的DBI抽象。我需要在`DBI->连接后执行一些操作。如何在DBI重新连接后挂钩

有没有办法在那里添加一个钩子而不修改这个模块?我没有在文档中找到它。我错过了什么?

+1

我假设您已阅读文档的[回调部分](https://metacpan.org/pod/DBI#Callbacks)?看起来你可以添加一个回调给任何方法*除了* connect!我所能想到的只是用'patch' DBI :: connect'来调用真正的'connect',然后传递它返回的'$ dbh'。 – Borodin

回答

3

DBI doc有a chapter about subclassing其中提到了一个不做任何事的$dbh->connected方法。这似乎正是你想要的。

当正在使用子类的话,后一个成功的新连接,该DBI->连接方法自动调用:

$dbh->connected($dsn, $user, $pass, \%attr); 

我还没有试过,但它可能是由刚刚工作的猴子 - 将此连接方法直接修改为DBI而不需要创建任何子类。 In connectthere is definitely a call to connected

但我不确定在哪里修补。可能进入驱动程序。快速grep of the cpan显示只有两个司机包括在DBI dist包括这一点。 DBD::GoferDBD::Proxy,但那个是空的。它们都在DBD::<drivername>::db包中。

假设你正在做MySQL,那么你会去挂钩你的驱动程序。可以通过继承和使用该驱动程序,或者通过简单的猴子修补它。

*DBD::mysql::db::connected = sub { 
    my ($dbh, dsn, $user, $pass, $attr, $old_driver) = @_; 

    warn 'Connected!'; 
} 

这应该与其他车手一样,除非他们有自己的connected。在这种情况下,您应该手动包装它或使用Class::Method::Modifiersaround之类的东西来包装它以确保原始行为保持不变。

此时您还有实际连接的$dbh,因此如果需要,您可以在数据库中搜索connected

当然这会给你回拨后每连接。如果你只想获得重新连接,你可以在一个词汇变量上创建一个闭包,这个变量计算连接并跳过第一个连接。

{ 
    my $connection_counter; 
    *DBD::mysql::db::connected = sub { 
     my ($dbh, dsn, $user, $pass, $attr, $old_driver) = @_; 

     return unless $connection_counter++; # skip first connection 
     warn 'Connected!'; 
    } 
} 

请注意,我还没有测试任何这一点。

+1

我希望你是对的,但OP说*“我处理一个模块,它提供了DBI的抽象”,他们不想修改,所以使用子类似乎是不可能的。我想子类化现有的子类可能会工作,如果这是抽象如何完成。 – Borodin

+0

并且谢谢。你教过我关于我迄今还没有意识到的'DBI'的一个角落。 – Borodin

+0

@Borodin我也没有意识到它。我很幸运浏览了文档。 ;) – simbabque