跳转至

Ruby 的 SQLite 事务

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

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

定义

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 在自动提交模式中,更改立即生效。 为了处理事务,我们使用transaction方法开始事务。 事务以commitrollback方法结束。

默认情况下,数据库连接处于自动提交模式。 请注意,默认模式取决于驱动程序。 在 SQLite Python 驱动程序中,默认情况下自动提交已关闭。

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

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

例子

现在,我们将有一些脚本可用于事务和自动提交模式。

#!/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表并用数据填充它。 我们没有明确启动事务,也没有调用commitrollback方法。 但是数据已写入表中。 这是因为默认的工作模式是自动提交。 在这种模式下,每个 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语句被保存。 第二个不是。

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



回到顶部