Perl 的 MySQL 事务
在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们介绍 Perl 脚本,该脚本显示如何在 Perl DBI 中处理事务。 我们还将讨论自动提交模式,这对于理解事务是必不可少的。
定义
事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 在自动提交模式中,更改立即生效。 要处理事务,我们要么关闭自动提交模式,要么使用begin_work()
方法启动事务。 事务以commit()
或rollback()
方法结束。
MySQL 数据库具有不同类型的存储引擎。 最常见的是 MyISAM 和 InnoDB 引擎。 在数据安全性和数据库速度之间需要权衡。 MyISAM 表的处理速度更快,并且不支持事务。 另一方面,InnoDB 表可以更安全地防止数据丢失。 他们支持事务。 它们处理较慢。
默认情况下,数据库连接处于自动提交模式。 AutoCommit
数据库句柄属性用于设置或读取自动提交模式。
当AutoCommit
打开时,对begin_work()
的调用将AutoCommit
关闭。 commit()
和rollback()
方法重新打开AutoCommit
。 如果关闭AutoCommit
属性,然后再调用begin_work()
方法,则会收到一条错误消息,提示我们已经在事务中。
例子
现在,我们将有一些可用于事务处理的脚本。
#!/usr/bin/perl
use strict;
use DBI;
my $dbh = DBI->connect(
"dbi:mysql:dbname=mydb",
"user12",
"34klq*",
{ RaiseError => 1, AutoCommit => 0 },
) or die $DBI::errstr;
$dbh->do("DROP TABLE IF EXISTS Friends");
$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY AUTO_INCREMENT,
Name TEXT) ENGINE=InnoDB");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");
$dbh->disconnect();
我们创建一个Friends
表,并尝试用数据填充它。 但是,正如我们将看到的,数据不会被提交。
{ RaiseError => 1, AutoCommit => 0 },
我们已经将AutoCommit
参数设置为 0。更改不会自动提交。 而且没有提交语句。 因此,更改不会写入数据库。
$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY AUTO_INCREMENT,
Name TEXT) ENGINE=InnoDB");
这是创建Friends
表的 SQL 语句。 我们已经指定了 InnoDB 引擎。 请注意,自 MySQL 5.5 起,默认引擎为 InnoDB。 在 MyISAM 表中,SQL 语句在执行后被提交。 MyISAM 表不支持事务。
$ ./noautocommit.pl
mysql> SELECT * FROM Friends;
Empty set (0.00 sec)
表已创建,但数据未插入表中。
在第二个示例中,我们将使用commit()
方法将数据写入数据库。
#!/usr/bin/perl
use strict;
use DBI;
my $dbh = DBI->connect(
"dbi:mysql:dbname=mydb",
"user12",
"34klq*",
{ RaiseError => 1, AutoCommit => 0 },
) or die $DBI::errstr;
$dbh->do("DROP TABLE IF EXISTS Friends");
$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY AUTO_INCREMENT,
Name TEXT) ENGINE=InnoDB");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");
$dbh->commit();
$dbh->disconnect();
当关闭自动提交模式时,每个语句都在事务内,直到我们调用commit()
方法为止。
$dbh->commit();
所有更改都将写入数据库。
mysql> SELECT * FROM Friends;
+----+---------+
| Id | Name |
+----+---------+
| 1 | Tom |
| 2 | Rebecca |
| 3 | Jim |
| 4 | Robert |
| 5 | Julian |
+----+---------+
5 rows in set (0.00 sec)
我们使用sqlite3
命令行工具验证是否已写入更改。
当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。
#!/usr/bin/perl
use strict;
use DBI;
my $dbh = DBI->connect(
"dbi:mysql:dbname=mydb",
"user12",
"34klq*",
{ RaiseError => 1, AutoCommit => 0},
) or die $DBI::errstr;
$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
$dbh->commit();
$dbh->disconnect();
在代码示例中,自动提交已关闭。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
表名称不正确。 数据库中没有Friend
表。
$ ./rollingback.pl
DBD::mysql::db do failed: Table 'mydb.Friend' doesn't exist at ./rollingback.pl line 14.
DBD::mysql::db do failed: Table 'mydb.Friend' doesn't exist at ./rollingback.pl line 14.
Issuing rollback() due to DESTROY without explicit disconnect() of
DBD::mysql::db handle dbname=mydb at ./rollingback.pl line 14.
运行示例将显示此错误消息。 事务回滚。
mysql> SELECT * FROM Friends;
+----+---------+
| Id | Name |
+----+---------+
| 1 | Tom |
| 2 | Rebecca |
| 3 | Jim |
| 4 | Robert |
| 5 | Julian |
+----+---------+
5 rows in set (0.00 sec)
即使第一个UPDATE
语句正确,在Friends
表中也没有发生任何变化。
正如我们在教程中已经提到的那样,默认模式是自动提交。 在这种模式下,我们可以使用begin_work()
方法开始新的事务,并使用commit()
或rollback()
完成它。 begin_work()
方法将关闭自动提交,commit()
和rollback()
方法将重新打开自动提交。
#!/usr/bin/perl
use strict;
use DBI;
my $dbh = DBI->connect(
"dbi:mysql:dbname=mydb",
"user12",
"34klq*",
{ RaiseError => 1, HandleError=>\&handle_error },
) or die $DBI::errstr;
$dbh->begin_work();
$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
$dbh->commit();
$dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");
$dbh->disconnect();
sub handle_error {
my $error = shift;
print "An error occurred in the script\n";
print "Message: $error\n";
return 1;
}
再次,我们有一个不正确的第二条 SQL 语句。 这次,我们没有明确关闭自动提交。
{ RaiseError => 1, HandleError=>\&handle_error },
我们将把错误处理委托给handle_error()
子例程。
$dbh->begin_work();
使用begin_work()
方法,我们开始一个新事务。 自动提交已关闭。
$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
这两个语句构成一个事务。 第二个是不正确的。
sub handle_error {
my $error = shift;
print "An error occurred in the script\n";
print "Message: $error\n";
return 1;
}
当我们遇到错误时,将调用此子例程。 我们打印一条错误消息。 请注意,该脚本不会退出。
$dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");
事务已回滚,我们没有退出脚本。 它继续。 回滚后,自动提交已重新打开。 新行已添加到Friends
表中。
$ ./rollingback2.pl
An error occurred in the script
Message: DBD::mysql::db do failed: Table 'mydb.Friend' doesn't exist
我们可以从handle_error()
子例程中看到我们的自定义错误消息。
mysql> SELECT * FROM Friends;
+----+---------+
| Id | Name |
+----+---------+
| 1 | Thomas |
| 2 | Rebecca |
| 3 | Jim |
| 4 | Robert |
| 5 | Julian |
| 6 | Ronald |
+----+---------+
6 rows in set (0.00 sec)
一个新朋友被插入了表。
在 MySQL Perl 教程的这一部分中,我们处理了事务。