# Perl 的 MySQL 事务

> 原文： [http://zetcode.com/db/mysqlperl/trans/](http://zetcode.com/db/mysqlperl/trans/)

在本章中，我们将处理事务。 首先，我们提供一些基本定义。 然后，我们介绍 Perl 脚本，该脚本显示如何在 Perl DBI 中处理事务。 我们还将讨论自动提交模式，这对于理解事务是必不可少的。

## 定义

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库，也可以全部回滚。 在自动提交模式中，更改立即生效。 要处理事务，我们要么关闭自动提交模式，要么使用`begin_work()`方法启动事务。 事务以`commit()`或`rollback()`方法结束。

MySQL 数据库具有不同类型的存储引擎。 最常见的是 MyISAM 和 InnoDB 引擎。 在数据安全性和数据库速度之间需要权衡。 MyISAM 表的处理速度更快，并且不支持事务。 另一方面，InnoDB 表可以更安全地防止数据丢失。 他们支持事务。 它们处理较慢。

默认情况下，数据库连接处于自动提交模式。 `AutoCommit`数据库句柄属性用于设置或读取自动提交模式。

当`AutoCommit`打开时，对`begin_work()`的调用将`AutoCommit`关闭。 `commit()`和`rollback()`方法重新打开`AutoCommit`。 如果关闭`AutoCommit`属性，然后再调用`begin_work()`方法，则会收到一条错误消息，提示我们已经在事务中。

## 例子

现在，我们将有一些可用于事务处理的脚本。

```perl
#!/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`表，并尝试用数据填充它。 但是，正如我们将看到的，数据不会被提交。

```perl
{ RaiseError => 1, AutoCommit => 0 }, 

```

我们已经将`AutoCommit`参数设置为 0。更改不会自动提交。 而且没有提交语句。 因此，更改不会写入数据库。

```perl
$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 表不支持事务。

```perl
$ ./noautocommit.pl

mysql> SELECT * FROM Friends;
Empty set (0.00 sec)

```

表已创建，但数据未插入表中。

在第二个示例中，我们将使用`commit()`方法将数据写入数据库。

```perl
#!/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()`方法为止。

```perl
$dbh->commit();

```

所有更改都将写入数据库。

```perl
mysql> SELECT * FROM Friends;
+----+---------+
| Id | Name    |
+----+---------+
|  1 | Tom     |
|  2 | Rebecca |
|  3 | Jim     |
|  4 | Robert  |
|  5 | Julian  |
+----+---------+
5 rows in set (0.00 sec)

```

我们使用`sqlite3`命令行工具验证是否已写入更改。

当事务中存在错误时，将回滚事务，并且不会将任何更改提交到数据库。

```perl
#!/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 语句中有错误。 因此，该事务将回滚。

```perl
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

```

表名称不正确。 数据库中没有`Friend`表。

```perl
$ ./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.

```

运行示例将显示此错误消息。 事务回滚。

```perl
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()`方法将重新打开自动提交。

```perl
#!/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 语句。 这次，我们没有明确关闭自动提交。

```perl
{ RaiseError => 1, HandleError=>\&handle_error },  

```

我们将把错误处理委托给`handle_error()`子例程。

```perl
$dbh->begin_work();

```

使用`begin_work()`方法，我们开始一个新事务。 自动提交已关闭。

```perl
$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

```

这两个语句构成一个事务。 第二个是不正确的。

```perl
sub handle_error {

    my $error = shift;
    print "An error occurred in the script\n";
    print "Message: $error\n";
    return 1;
}

```

当我们遇到错误时，将调用此子例程。 我们打印一条错误消息。 请注意，该脚本不会退出。

```perl
$dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");

```

事务已回滚，我们没有退出脚本。 它继续。 回滚后，自动提交已重新打开。 新行已添加到`Friends`表中。

```perl
$ ./rollingback2.pl
An error occurred in the script
Message: DBD::mysql::db do failed: Table 'mydb.Friend' doesn't exist

```

我们可以从`handle_error()`子例程中看到我们的自定义错误消息。

```perl
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 教程的这一部分中，我们处理了事务。