【问题标题】:Creating a database module in Perl在 Perl 中创建数据库模块
【发布时间】:2014-09-26 02:34:33
【问题描述】:

我想知道这是否是创建数据库模块的正确方法,尤其是创建和使用数据库句柄的方式:

use strict;
use warnings;
use DBI;

my $DB_NAME = 'dancerapp';
my $DB_USER = 'root';
my $DB_PASS = 'root';

sub new {
    my $class = shift;
    return {}, $class;
}

sub _connect { #use connect_cached() instead?
    my $dbh = DBI->connect($DSN, $DB_USER, $DB_PASS) or die $DBI::errstr;
    return $dbh;
}

sub getTickets {
    my $self = shift;
    my $ticket_holder = shift;
    my $dbh = $self->_connect;
    my $sth = $dbh->prepare("SELECT * FROM table WHERE assigned_to=?");
    $sth->execute($ticket_holder);
    return $sth->fetchall_hashref;
}

1;

将所有数据库查询存储在一个模块中的主要目的是为它们提供一个单独的位置,我只关心有多个连接/数据库句柄到处乱跑

【问题讨论】:

    标签: perl perl-module dbi


    【解决方案1】:

    在 Perl 传统中,有很多方法可以做到这一点。但我会尽可能多地将一次性操作移到构造函数中。

    您的代码存在一些具体问题

    • 您的模块需要一个包(类)名称

    • 您的构造函数需要bless 哈希引用,而不仅仅是return

    • 你没有定义$DSN

    • 您的getTickets 方法没有$dbh 可供使用。调用DBI->connect时,句柄应该存储在对象中

    除此之外,我认为没有理由使用单独的 connect 方法。那是你可以移动到构造方法中的东西

    每次要执行查询时重复调用prepare 也是没有意义的。相反,使用||= 缓存语句以节省经常使用的查询的处理时间。

    最后,最好将数据库名称、用户和作为参数传递给new。这样您就可以将同一个模块用于多个数据库,而无需通过编辑模块来更改环境。

    这就像我要写的那样。请注意,除了确保它可以编译之外,它还未经测试。使用一段时间后,您可能会发现您希望将更多信息移动到对象中 - 例如 DSN - 如果只是为了帮助调试。

    package MyDB;
    
    use strict;
    use warnings;
    
    use DBI;
    
    sub new {
        my $class = shift;
    
        my $self = {};
        @{$self}{qw/ db user pass /} = @_;
    
        my $dsn = "DBI:mysql:database=$self->{db}";
        $self->{dbh} = DBI->connect($dsn, @{$self}{qw/ user pass /}) or die $DBI::errstr;
    
        bless $self, $class;
    }
    
    sub get_tickets {
        my $self = shift;
        my ($ticket_holder) = @_;
    
        $self->{get_tickets} ||= $self->{dbh}->prepare('SELECT * FROM table WHERE assigned_to = ?');
        $self->{get_tickets}->execute($ticket_holder);
    
        $self->{get_tickets}->fetchall_hashref;
    }
    
    1;
    

    【讨论】:

    • 我以前从没想过将我的所有语句都放在构造函数中,但现在这完全有道理,因为如果它是相同的,你不会想要多次准备一些东西。也感谢 OO 更正:)
    • 实际上,不是将它们放入构造函数中,而是使用||= 缓存查询,这样它们就不会被重新计算。这更好,因为它将准备好的语句留在实际使用它们的代码附近,而不是在构造函数中隔离。
    【解决方案2】:

    创建数据库模块没有绝对的“正确方法”。您所采用的方法似乎是一个很好的起点,尤其是如果它以当前的形式适合您,但是您可能需要根据自己的需要进行一些迭代来完善模块及其功能。

    如果我可以提供一些自以为是的指针:

    • 拥有一个用于处理数据库连接的参考模块是一个好主意。如果您需要获得广泛采用,请尽可能轻松地使用“开箱即用”。您不希望发生“XKCD: Standards”这种情况。

    • 将所有数据库查询放在一个模块中也有一些优点,但是根据您的需求增长幅度,您可能很快就会遇到一个无法维护的数千行代码的怪物。因此,从一开始就将它们排序/分类到较小的子模块中可能会让您免于痛苦。

    • 进一步推动之前的评论,如果查询与系统中某些对象的行为直接相关,请考虑改用 DBIx::Class 等 ORM。

    • 将配置(DSN、用户名、密码)与模块分开。这对于不意外地针对生产实例测试您的前沿开发特别有用。

    • 使用一组专用的显式参数传递给DBI 和底层驱动程序(例如AutoCommmit => 0),可选择允许用户覆盖一些参数。

    • 如果您的 DBMS 支持事务,请通过始终提交或回滚它们来确保您的模块不会让松散的事务落后。或者,您可以让用户负责明确使用提交或回滚,如果他们不这样做,die() 真的很难。当许多其他并发模块使用数据库时,这将为您节省很多痛苦。

    • connect_cache() 有优点,但它可能会带来自己的一组并发问题。

    此外,如果您的模块旨在用于 Web 应用程序,请注意 Apache::DBI 可能会随意使用您的连接方式。

    【讨论】:

    • 谢谢,这很有帮助!我认为在这里接受一个答案而不是另一个答案是正确的,因为这些都非常有帮助并回答了我的问题:)
    • @a7omiton:请记住,您提出问题的主要目的是帮助有类似问题的其他人。因此,最好接受一个答案,以便将问题标记为已解决。但您可能需要推迟一天左右,以防出现更好的解决方案。
    猜你喜欢
    • 2012-07-20
    • 2018-01-15
    • 2010-10-07
    • 2014-11-12
    • 2013-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    相关资源
    最近更新 更多