在本章中,我们将展示如何处理错误。
方法名称 | 描述 |
---|---|
$h->err() |
从上次调用的驱动程序方法返回本机数据库引擎错误代码。 |
$h->errstr() |
从最后一个调用的 DBI 方法返回本机数据库引擎错误消息。 |
$h->state() |
以标准SQLSTATE 五字符格式返回状态代码。 |
以上三种方法处理错误消息。
DBI 动态属性 | 描述 |
---|---|
$DBI::err |
相当于$h->err() |
$DBI::errstr |
相当于$h->errstr() |
$DBI::state |
相当于$h->state() |
第二张表列出了与错误处理有关的 DBI 动态属性。 这些属性的寿命很短。 应该在可能导致错误的方法之后立即使用它们。
默认错误处理
默认情况下,错误由 Perl DBI 方法返回。
#!/usr/bin/perl
use strict;
use DBI;
my dsn = "dbi:mysql:dbname=mydb";
myuser = "user12";
my password = "34klq*";
mydbh = DBI->connect(dsn,user, password) or die "Can't connect to database:DBI::errstr";
my sth =dbh->prepare(
q{ SELECT Id, Name, Price FROM Cars }
)
or die "Can't prepare statement: DBI::errstr";
myrc = sth->execute()
or die "Can't execute statement:DBI::errstr";
while (my(id,name, price) =sth->fetchrow()) {
print "idname price\n";
}
# check for problems which may have terminated the fetch early
warnDBI::errstr if DBI::err;sth->finish();
$dbh->disconnect();
在第一个脚本中,我们处理返回错误代码的默认行为。
my dbh = DBI->connect(dsn, user,password)
or die "Can't connect to database: $DBI::errstr";
我们调用connect()
方法来创建数据库连接。 如果尝试失败,则该方法返回undef
并设置$DBI::err
和$DBI::errstr
属性。 die()
方法在失败的情况下会打印错误消息并终止脚本。
my sth =dbh->prepare(
q{ SELECT Id, Name, Price FROM Cars }
)
or die "Can't prepare statement: $DBI::errstr";
我们称为prepare()
语句。 如果该方法失败,则die()
方法将显示错误消息并终止脚本。
my rc =sth->execute()
or die "Can't execute statement: $DBI::errstr";
再次。 我们调用execute()
方法并检查错误。 如果失败,该方法将返回undef
。
warn DBI::errstr ifDBI::err;
我们检查可能会提早终止提取方法的问题。
引发异常
每次我们调用 DBI 方法时都要检查错误,这可能很麻烦。 如果我们有一个较大的脚本,我们很容易忘记这样做。 处理可能的错误的首选方法是引发异常。 为了引发异常,我们将RaiseError
属性设置为 true。
#!/usr/bin/perl
use strict;
use DBI;
my dsn = "dbi:mysql:dbname=mydb";
myuser = "user12";
my password = "34klq*";
my %attr = ( RaiseError => 1 );
mydbh = DBI->connect(dsn,user, password, \%attr) or die "Can't connect to database:DBI::errstr";
my sth =dbh->prepare("SELECT * FROM Cars LIMIT 5");
sth->execute();
while (my(id, name,price) = sth->fetchrow()) {
print "id nameprice\n";
}
sth->finish();dbh->disconnect();
在连接属性中,我们将RaiseError
属性设置为 1。发生错误时,引发异常,而不是返回错误代码。
my %attr = ( RaiseError => 1 );
我们将 RaiseError 属性设置为 1。
my dbh = DBI->connect(dsn, user,password, \%attr)
or die "Can't connect to database: $DBI::errstr";
connect()
方法是我们检查返回代码的唯一方法。
my sth =dbh->prepare("SELECT * FROM Cars LIMIT 5");
$sth->execute();
prepare()
和execute()
方法不检查返回错误代码。 如果失败,将引发异常,Perl DBI 将调用die()
方法并打印错误消息。
错误子程序
使用 HandleError 连接的 handle 属性,我们可以设置对子例程的引用,该子例程在检测到错误时被调用。 用三个参数调用该子例程:RaiseError 和“ PrintError”将使用的错误消息字符串,正在使用的 DBI 句柄以及失败的方法返回的第一个值(通常为undef
)。
如果子例程返回错误值,则将检查RaiseError
或PrintError
属性,并按常规操作。
#!/usr/bin/perl
use strict;
use DBI;
my dsn = "dbi:mysql:dbname=mydb";
myuser = "user12";
my password = "34klq*";
my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error );
mydbh = DBI->connect(dsn,user, password, \%attr) or die "Can't connect to database:DBI::errstr";
dbh->do("UPDATE Cars SET Price=52000 WHERE Id=1");dbh->do("UPDATE Car SET Price=22000 WHERE Id=8");
dbh->commit();dbh->disconnect();
sub handle_error {
dbh->rollback();
myerror = shift;
print "An error occurred in the script\n";
print "Message: $error\n";
return 1;
}
我们自己的子例程将处理该错误。
my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error );
当检测到错误时,HandleError
属性提供对所调用的handle_error()
子例程的引用。 AutoCommit
已关闭,这意味着我们正在处理事务。
$dbh->do("UPDATE Car SET Price=22000 WHERE Id=8");
SQL 语句中有错误。 没有汽车桌子。
sub handle_error {
dbh->rollback();
myerror = shift;
print "An error occurred in the script\n";
print "Message: $error\n";
return 1;
}
这是handle_error()
子例程。 我们打印错误消息。 并返回 1。如果我们改为返回 0,则会出现其他错误消息。 禁止返回与RaiseError
属性相关的 1 条错误消息。
$ ./errsub.pl
An error occurred in the script
Message: DBD::mysql::db do failed: Table 'mydb.Car' doesn't exist
示例的输出。
eval
的例子
根据 Perl DBI 文档,处理 DBI 错误的最可靠的方法是使用eval()
方法。
#!/usr/bin/perl
use strict;
use DBI;
use DBI qw(:sql_types);
my dsn = "dbi:mysql:dbname=mydb";
myuser = "user12";
my password = "34klq*";
my %attr = ( RaiseError => 1, AutoCommit => 0 );
mydbh = DBI->connect(dsn,user, password, \%attr) or die "Can't connect to database:DBI::errstr";
my @data = (
[1, "Audi", 52642],
[2, "Mercedes", 57127],
[3, "Skoda", 9000],
[4, "Volvo", 29000],
[5, "Bentley", 350000],
[6, "Citroen", 21000],
[7, "Hummer", 41400],
[8, "Volkswagen", 21601]
);
eval {
dbh->do("DROP TABLE IF EXISTS Cars");dbh->do("CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INT)");
};
my sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) };
mysth = dbh->prepare(sql);
foreach my row (@data) {
eval {sth->bind_param(1, @row[0], SQL_INTEGER);sth->bind_param(2, @row[1], SQL_VARCHAR);sth->bind_param(3, @row[2], SQL_INTEGER);sth->execute();
dbh->commit();
};
if(@ ) {
warn "Database error: DBI::errstr\n";dbh->rollback();
}
}
sth->finish();dbh->disconnect();
上面的代码示例是处理错误的最正确方法。
my %attr = ( RaiseError => 1, AutoCommit => 0 );
我们提出异常,而不是检查返回码。 我们关闭自动提交模式,然后手动提交或回滚更改。
my sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) };
mysth = dbh->prepare(sql);
我们使用占位符来防止错误和安全问题。
eval {
sth->bind_param(1, @row[0], SQL_INTEGER);
sth->bind_param(2, @row[1], SQL_VARCHAR);
sth->bind_param(3, @row[2], SQL_INTEGER);
sth->execute();dbh->commit();
};
在eval()
方法内部,我们放置了容易出错的代码。 该方法捕获异常,并用错误消息填充$@
特殊变量。 我们使用bind_param()
方法将变量绑定到占位符。
if( @ ) {
warn "Database error:DBI::errstr\n";
$dbh->rollback();
}
如果发生错误,我们将打印错误消息并回滚更改。