在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们提供 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
表并用数据填充它。 我们没有明确启动事务,也没有调用 commit 或 rollback 方法。 但是数据已写入表中。 这是因为默认的工作模式是自动提交。 在这种模式下,每个 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
方法开始新的事务。 该方法采用可选的 mode 参数,我们可以在其中指定事务级别。 默认级别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
即使第一个 UPDATE 语句正确,Friends 表也没有更改。
我们将再次尝试更改两行,这次是在自动提交模式下。
#!/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
语句被保存,第二个不是。