Ruby 中的 SQLite 事务
最后修改于 2020 年 7 月 6 日
在本章中,我们将使用事务。首先,我们提供一些基本定义。然后,我们展示 Ruby 脚本,这些脚本演示了如何在 Ruby sqlite3
模块中使用事务。我们还将讨论自动提交模式,这对于理解事务至关重要。
定义
事务是对一个或多个数据库中数据的原子数据库操作单元。事务中所有 SQL 语句的效果要么全部提交到数据库,要么全部回滚。在自动提交模式下,更改立即生效。要使用事务,我们使用 transaction
方法启动一个事务。事务以 commit
或 rollback
方法结束。
数据库连接默认处于自动提交模式。请注意,默认模式取决于驱动程序。在 SQLite Python 驱动程序中,默认关闭自动提交。
在 SQLite 中,除 SELECT
之外的任何命令都将启动一个隐式事务。此外,在一个事务中,像 CREATE TABLE
..., VACUUM
, PRAGMA
这样的命令将在执行之前提交之前的更改。手动事务使用 BEGIN TRANSACTION
语句启动,并使用 COMMIT
或 ROLLBACK
语句结束。
SQLite 支持三个非标准事务级别:DEFERRED
、IMMEDIATE
和 EXCLUSIVE
。
示例
现在我们将有一些使用事务和自动提交模式的脚本。
#!/usr/bin/ruby require 'sqlite3' begin db = SQLite3::Database.open "test.db" db.execute "DROP TABLE IF EXISTS Friends" db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)" db.execute "INSERT INTO Friends(Name) VALUES ('Tom')" db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')" db.execute "INSERT INTO Friends(Name) VALUES ('Jim')" db.execute "INSERT INTO Friends(Name) VALUES ('Robert')" db.execute "INSERT INTO Friends(Name) VALUES ('Julian')" rescue SQLite3::Exception => e puts "Exception occurred" puts e ensure db.close if db end
我们创建一个 Friends
表并用数据填充它。我们没有显式地启动事务,也没有调用提交或回滚方法。然而,数据被写入到表中。这是因为默认工作模式是自动提交。在这种模式下,每个 SQL 语句都立即生效。
db.execute "DROP TABLE IF EXISTS Friends" db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"
如果 Friends
表已经存在,我们会删除它。然后我们用 CREATE TABLE
语句创建表。
db.execute "INSERT INTO Friends(Name) VALUES ('Tom')" db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')" ...
我们插入数据。
$ ./autocommit.rb $ sqlite3 test.db SQLite version 3.7.7 2011-06-23 19:49:22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> SELECT * FROM Friends; 1|Tom 2|Rebecca 3|Jim 4|Robert 5|Julian
我们执行脚本并使用 sqlite3
命令行工具检查表。 Friends
表已成功创建。
在第二个示例中,我们将使用 transaction
方法启动一个事务。
#!/usr/bin/ruby require 'sqlite3' begin db = SQLite3::Database.open "test.db" db.transaction db.execute "DROP TABLE IF EXISTS Friends" db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)" db.execute "INSERT INTO Friends(Name) VALUES ('Tom')" db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')" db.execute "INSERT INTO Friends(Name) VALUES ('Jim')" db.execute "INSERT INTO Friends(Name) VALUES ('Robert')" db.execute "INSERT INTO Friends(Name) VALUES ('Julian')" db.execute "INSERT INTO Friends(Name) VALUES ('Michael')" db.commit rescue SQLite3::Exception => e puts "Exception occurred" puts e db.rollback ensure db.close if db end
我们重新创建 Friends
表。在调用 transaction
方法之后,每个语句都在一个事务中,直到我们调用 commit
方法。我们要么保存所有更改,要么什么都不保存。这是事务背后的基本思想。
db.transaction
transaction
方法开始一个新事务。该方法接受一个可选的模式参数,我们可以在其中指定事务级别。默认级别为 DEFERRED
。
db.commit
更改被写入到数据库。如果我们注释掉该行,则不会保存更改。
db.rollback
如果发生错误,我们会回滚更改。
sqlite> SELECT * FROM Friends; 1|Tom 2|Rebecca 3|Jim 4|Robert 5|Julian 6|Michael
我们使用 sqlite3
命令行工具验证更改是否被写入。
当事务中发生错误时,事务会被回滚,并且不会将任何更改提交到数据库。
#!/usr/bin/ruby require 'sqlite3' begin db = SQLite3::Database.open "test.db" db.transaction db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1" db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4" db.commit rescue SQLite3::Exception => e puts "Exception occurred" puts e db.rollback ensure db.close if db end
在代码示例中,我们想更改两个名称。有两个语句构成一个事务。第二个 SQL 语句中存在错误。因此,事务被回滚。
db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"
表的名称不正确。数据库中没有 Friend 表。
$ ./rollingback.rb Exception occurred no such table: Friend
运行该示例将显示此错误消息。事务被回滚。
sqlite> SELECT * FROM Friends; 1|Tom 2|Rebecca 3|Jim 4|Robert 5|Julian
Friends 表未更改,即使第一个 UPDATE 语句是正确的。
我们将再次尝试更改两行,这次是在自动提交模式下。
#!/usr/bin/ruby require 'sqlite3' begin db = SQLite3::Database.new "test.db" db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1" db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4" rescue SQLite3::Exception => e puts "Exception occurred" puts e ensure db.close if db end
我们尝试在 Friends
表中更新两个名称,将 Tom 更改为 Thomas,将 Robert 更改为 Bob。
db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1" db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"
第二个 UPDATE
语句不正确。
$ ./autocommit2.rb Exception occurred no such table: Friend
我们收到与前一个示例相同的错误消息。
sqlite> SELECT * FROM Friends; 1|Thomas 2|Rebecca 3|Jim 4|Robert 5|Julian
但是这次,第一个 UPDATE
语句被保存。第二个语句没有被保存。
在 SQLite Ruby 教程的这一部分中,我们使用了事务。