原文: http://zetcode.com/db/sqliteperltutorial/trans/

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

定义

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

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

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

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE …,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。 手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE

例子

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

  1. #!/usr/bin/perl
  2. use strict;
  3. use DBI;
  4. my $dbh = DBI->connect(
  5. "dbi:SQLite:dbname=test.db",
  6. "",
  7. "",
  8. { RaiseError => 1, AutoCommit => 0 },
  9. ) or die $DBI::errstr;
  10. $dbh->do("DROP TABLE IF EXISTS Friends");
  11. $dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");
  12. $dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
  13. $dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
  14. $dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
  15. $dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
  16. $dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");
  17. $dbh->disconnect();

我们创建一个Friends表,并尝试用数据填充它。 但是,正如我们将看到的,数据不会被提交。

  1. { RaiseError => 1, AutoCommit => 0 },

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

  1. $ ./noautocommit.pl
  2. $ sqlite3 test.db
  3. SQLite version 3.7.7 2011-06-23 19:49:22
  4. Enter ".help" for instructions
  5. Enter SQL statements terminated with a ";"
  6. sqlite> .tables
  7. Cars Images

我们的数据库中没有Friends表。

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

  1. #!/usr/bin/perl
  2. use strict;
  3. use DBI;
  4. my $dbh = DBI->connect(
  5. "dbi:SQLite:dbname=test.db",
  6. "",
  7. "",
  8. { RaiseError => 1, AutoCommit => 0},
  9. ) or die $DBI::errstr;
  10. $dbh->do("DROP TABLE IF EXISTS Friends");
  11. $dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");
  12. $dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
  13. $dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
  14. $dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
  15. $dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
  16. $dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");
  17. $dbh->commit();
  18. $dbh->disconnect();

当关闭自动提交模式时,每个语句都在事务内,直到我们调用commit()方法为止。

  1. $dbh->commit();

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

  1. sqlite> SELECT * FROM Friends;
  2. 1|Tom
  3. 2|Rebecca
  4. 3|Jim
  5. 4|Robert
  6. 5|Julian

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

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

  1. #!/usr/bin/perl
  2. use strict;
  3. use DBI;
  4. my $dbh = DBI->connect(
  5. "dbi:SQLite:dbname=test.db",
  6. "",
  7. "",
  8. { RaiseError => 1, AutoCommit => 0},
  9. ) or die $DBI::errstr;
  10. $dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
  11. $dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
  12. $dbh->commit();
  13. $dbh->disconnect();

在代码示例中,自动提交已关闭。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。

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

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

  1. $ ./rollingback.pl
  2. DBD::SQLite::db do failed: no such table: Friend at ./rollingback.pl line 15.
  3. DBD::SQLite::db do failed: no such table: Friend at ./rollingback.pl line 15.
  4. Issuing rollback() due to DESTROY without explicit disconnect()
  5. of DBD::SQLite::db handle dbname=test.db at ./rollingback.pl line 15.

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

  1. $ sqlite3 test.db
  2. SQLite version 3.7.7 2011-06-23 19:49:22
  3. Enter ".help" for instructions
  4. Enter SQL statements terminated with a ";"
  5. sqlite> SELECT * FROM Friends;
  6. 1|Tom
  7. 2|Rebecca
  8. 3|Jim
  9. 4|Robert
  10. 5|Julian
  11. sqlite>

即使第一个UPDATE语句正确,在Friends表中也没有发生任何变化。

正如我们在教程中已经提到的那样,默认模式是自动提交。 在这种模式下,我们可以使用begin_work()方法开始新的事务,并使用commit()rollback()完成它。 begin_work()方法将关闭自动提交,commit()rollback()方法将重新打开自动提交。

  1. #!/usr/bin/perl
  2. use strict;
  3. use DBI;
  4. my $dbh = DBI->connect(
  5. "dbi:SQLite:dbname=test.db",
  6. "",
  7. "",
  8. { RaiseError => 1, HandleError=>\&handle_error },
  9. ) or die $DBI::errstr;
  10. $dbh->begin_work();
  11. $dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
  12. $dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");
  13. $dbh->commit();
  14. $dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");
  15. $dbh->disconnect();
  16. sub handle_error {
  17. my $error = shift;
  18. print "An error occurred in the script\n";
  19. print "Message: $error\n";
  20. return 1;
  21. }

再次,我们有一个不正确的第二条 SQL 语句。 这次,我们没有明确关闭自动提交。

  1. { RaiseError => 1, HandleError=>\&handle_error },

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

  1. $dbh->begin_work();

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

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

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

  1. sub handle_error {
  2. my $error = shift;
  3. print "An error occurred in the script\n";
  4. print "Message: $error\n";
  5. return 1;
  6. }

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

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

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

  1. $ ./rollingback2.pl
  2. An error occurred in the script
  3. Message: DBD::SQLite::db do failed: no such table: Friend

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

  1. sqlite> SELECT * FROM Friends;
  2. 1|Thomas
  3. 2|Rebecca
  4. 3|Jim
  5. 4|Robert
  6. 5|Julian
  7. 6|Ronald

一个新朋友被插入了表。

在 SQLite Perl 教程的这一部分中,我们处理了事务。