反馈、有关我的模块的问题以及我是否应该更改任何内容?
package My::Module;
# $Id$
use strict;
use Carp;
use Data::Dumper;
use DBI;
$My::Module::VERSION = '0.1';
sub new {
my ($class, %opt) = @_;
my $opt_count = keys %opt;
$class->set_error('');
#return $class->set_error("Too many arguments to initialize.") if ($opt_count > 5);
#return $class->set_error("Missing arguments to initialize.") if ($opt_count < 2);
my $self = bless {
_DRIVER_OPTIONS => $opt{'mysql'},
},$class;
if (not defined $self) {
return $class->set_error( "new() failed: " . $class->errstr );
}
if ($self->{_DRIVER_OPTIONS}->{Host} ne '') {
$self->{_DRIVER_OPTIONS}->{DataSource} = 'DBI:mysql:database=' . $self->{_DRIVER_OPTIONS}->{Database} . ';host=' . $self->{_DRIVER_OPTIONS}->{Host};
} else {
$self->{_DRIVER_OPTIONS}->{DataSource} = 'DBI:mysql:database=' . $self->{_DRIVER_OPTIONS}->{Database} . ';';
}
$self->{Handle} = DBI->connect($self->{_DRIVER_OPTIONS}->{DataSource},
$self->{_DRIVER_OPTIONS}->{Username},
$self->{_DRIVER_OPTIONS}->{Password},
{ RaiseError=>1, PrintError=>1, AutoCommit=>1 }
);
return $self->set_error("new(): couldn't connect to database: " . DBI->errstr) unless ($self->{Handle});
$self->{_disconnect} = 1;
print Dumper \$self;
return $self;
}
sub database {
my $self = shift;
if (@_) { $self->{Handle} = shift }
return $self->{Handle};
}
sub set_error {
my $class = shift;
my $message = shift;
$class = ref($class) || $class;
no strict 'refs';
${ "$class\::errstr" } = sprintf($message || "", @_);
return;
}
*error = \&errstr;
sub errstr {
my $class = shift;
$class = ref( $class ) || $class;
no strict 'refs';
return ${ "$class\::errstr" } || '';
}
sub DESTROY {
my $self = shift;
unless (defined $self->{Handle} && $self->{Handle}->ping) {
$self->set_error(__PACKAGE__ . '::DESTROY(). Database handle has gone away');
return;
}
unless ($self->{Handle}->{AutoCommit}) {
$self->{Handle}->commit;
}
if ($self->{_disconnect}) {
$self->{Handle}->disconnect;
}
}
1;
这是正确的方式吗? 在我的代码中重新使用数据库 而不必打开一个新的 连接或也将打开 每次使用时都会有一个新连接 ?
我应该改变什么吗? 模块 ?或者我做错了什么?
目前我正在学习并考虑做我自己的引擎模块,所以我从这个开始。
简单的测试代码(以下代码不予审查,只是一个如何使用该模块的示例):
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use lib 'path to module';
use My::Module;
my $session = My::Module->new(mysql => {
Database =>'module',
Host =>'10.0.0.2',
Username =>'module',
Password =>'module'
}) or die My::Module->errstr;
my $dbh = $session->database();
my $sth = $dbh->prepare(q{
SELECT session_id
FROM sessions
});
$sth->execute() || die print($dbh->errstr);
my $ref = $sth->fetchall_arrayref({});
$sth->finish;
print Dumper \$ref;
package My::Module;
# $Id$
use strict;
use Carp;
use Data::Dumper;
use DBI;
$My::Module::VERSION = '0.1';
sub new {
my ($class, %opt) = @_;
my $opt_count = keys %opt;
$class->set_error('');
#return $class->set_error("Too many arguments to initialize.") if ($opt_count > 5);
#return $class->set_error("Missing arguments to initialize.") if ($opt_count < 2);
my $self = bless {
_DRIVER_OPTIONS => $opt{'mysql'},
},$class;
if (not defined $self) {
return $class->set_error( "new() failed: " . $class->errstr );
}
if ($self->{_DRIVER_OPTIONS}->{Host} ne '') {
$self->{_DRIVER_OPTIONS}->{DataSource} = 'DBI:mysql:database=' . $self->{_DRIVER_OPTIONS}->{Database} . ';host=' . $self->{_DRIVER_OPTIONS}->{Host};
} else {
$self->{_DRIVER_OPTIONS}->{DataSource} = 'DBI:mysql:database=' . $self->{_DRIVER_OPTIONS}->{Database} . ';';
}
$self->{Handle} = DBI->connect($self->{_DRIVER_OPTIONS}->{DataSource},
$self->{_DRIVER_OPTIONS}->{Username},
$self->{_DRIVER_OPTIONS}->{Password},
{ RaiseError=>1, PrintError=>1, AutoCommit=>1 }
);
return $self->set_error("new(): couldn't connect to database: " . DBI->errstr) unless ($self->{Handle});
$self->{_disconnect} = 1;
print Dumper \$self;
return $self;
}
sub database {
my $self = shift;
if (@_) { $self->{Handle} = shift }
return $self->{Handle};
}
sub set_error {
my $class = shift;
my $message = shift;
$class = ref($class) || $class;
no strict 'refs';
${ "$class\::errstr" } = sprintf($message || "", @_);
return;
}
*error = \&errstr;
sub errstr {
my $class = shift;
$class = ref( $class ) || $class;
no strict 'refs';
return ${ "$class\::errstr" } || '';
}
sub DESTROY {
my $self = shift;
unless (defined $self->{Handle} && $self->{Handle}->ping) {
$self->set_error(__PACKAGE__ . '::DESTROY(). Database handle has gone away');
return;
}
unless ($self->{Handle}->{AutoCommit}) {
$self->{Handle}->commit;
}
if ($self->{_disconnect}) {
$self->{Handle}->disconnect;
}
}
1;
Is that the right way so i can
re-use the database on my code
instead of having to open a new
connection or that will aswell open
a new connection every time i use it
?Should i change anything on the
module ? or anything i did wrong ?
Currently i am just learning and thinked of doing my own engine module so i began with this.
Simple test code (the bellow code is not to be reviewed just a sample on how to use the module):
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use lib 'path to module';
use My::Module;
my $session = My::Module->new(mysql => {
Database =>'module',
Host =>'10.0.0.2',
Username =>'module',
Password =>'module'
}) or die My::Module->errstr;
my $dbh = $session->database();
my $sth = $dbh->prepare(q{
SELECT session_id
FROM sessions
});
$sth->execute() || die print($dbh->errstr);
my $ref = $sth->fetchall_arrayref({});
$sth->finish;
print Dumper \$ref;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我建议使用现有的数据库接口,而不是自己开发,因为有许多秘密问题是其他人花了数年时间才为您找出并解决的。 DBIx::Connector 非常出色,并且具有其
修复
模式,将允许您重用数据库连接,甚至跨进程分支。此外,如果您使用 Moose,您将永远不必再次编写自己的对象构造函数或对象字段。 :)
DBIx::Class 与 Moose 结合会更好,但不是必需的直到您发现自己需要更多 ORM 风格的功能。
I would suggest using an existing database interface rather than rolling your own, as there are many secret gotchas that others have spent years figuring out and solving for you. DBIx::Connector is excellent, and with its
fixup
mode, will let you reuse database connections, even across process forks.Additionally, if you use Moose you will never have to write your own object constructors or object fields again. :)
DBIx::Class combined with Moose would be even better, but not necessary until you find yourself needing more ORM-ish features.
除了使用 CPAN 模块来完成此任务之外,以下是我的实用建议:
如果您班级的用户未启用
AutoCommit
,她选择不启用AutoCommit
是有原因的。因此不要这样做:在
DESTROY
中。open
的行为可能无法打开文件,即使open
的参数是有效的文件名,并且会通过返回错误值来指示这种情况)。因此,检查bless
的返回值没有任何用处。如果您想处理bless
失败的可能性,则必须捕获致命的运行时异常。Other than using a CPAN module to accomplish this task, here are my practical recommendations:
If the user of your class did not enable
AutoCommit
, she chose not to enableAutoCommit
for a reason. Therefore don't do:in
DESTROY
.open
which can fail to open a file even though the argument toopen
is a valid filename and would indicate this situation by returning a false value). Therefore, checking the return value ofbless
does not serve any useful purpose. If you want to handle the possibility ofbless
failing, you will have to catch fatal runtime exceptions.你暴露错误的方式非常非常过时。如果发生异常情况,为什么不提出适当的异常呢?您似乎已经在
DBI
模块之后对错误处理进行了建模。请注意,DBI
还有一个RaiseError
选项。使用它几乎总是比使用老式的 errorstr 版本更合理。不幸的是,DBI 现在无法再更改它的默认值,但对于新代码,我完全不认为有理由复制这个有缺陷的想法。您还可以根据用户从外部提供的参数在代码中构建 DBI 连接。你有充分的理由这样做吗?允许用户传入自己构造的
DBI::dh
会更加灵活。是的,这需要更多的外部代码来设置对象并将它们连接在一起,但它也会带来更简洁的设计。如果手动连接对象对您来说太麻烦,您可能需要看看Bread::Board
为您进行接线,而不是影响模块的设计。另外,我赞同 Ether 使用
DBIx::Connector
的建议。管理数据库句柄确实很麻烦,而且很容易出错。Your way of exposing errors is very, very oldfashioned. If something exceptional happens, why not raise a proper exception? You seem to have modelled your error handling after the
DBI
module. Note thatDBI
also has aRaiseError
option. Using that is almost always more reasonable than using the oldfashionederrorstr
version. Unfortunately DBI can't change it's default anymore now, but for new code I entirely don't see the reason to copy this flawed idea.You're also constructing a DBI connection within your code, based on parameters the user provided from the outside. Do you have a good reason for doing that? Allowing the user to pass in the
DBI::dh
he constructed himself would be more flexible. Yes, that requires slightly more code on the outside to set up objects and wire them together, but it will also result in a cleaner design. If wiring up your objects manually bothers you too much, you might want to have a look atBread::Board
to do the wiring for you instead of compromising on the design of your module.Also, I second Ether's suggestion of using
DBIx::Connector
. It really takes a lot of pain out of managing database handles, which can be very error-prone.