# Ruby 的 SQLite 事务

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

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

## 定义

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

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

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

SQLite 支持三种非标准事务级别：`DEFERRED`，`IMMEDIATE`和`EXCLUSIVE`。

## 例子

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

```ruby
#!/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 语句立即生效。

```ruby
db.execute "DROP TABLE IF EXISTS Friends"
db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"

```

如果`Friends`表已经存在，我们将其删除。 然后，我们使用`CREATE TABLE`语句创建表。

```ruby
db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
...

```

我们插入数据。

```ruby
$ ./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`方法启动事务。

```ruby
#!/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`方法为止。 我们要么保存所有更改，要么不保存任何内容。 这是事务背后的基本思想。

```ruby
db.transaction

```

`transaction`方法开始新的事务。 该方法采用可选的`mode`参数，我们可以在其中指定事务级别。 默认级别`DEFERRED`。

```ruby
db.commit

```

所做的更改将写入数据库。 如果我们注释了这一行，则更改将不会保存。

```ruby
db.rollback

```

如果发生错误，我们将回滚更改。

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

```

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

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

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

```ruby
db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"

```

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

```ruby
$ ./rollingback.rb
Exception occurred
no such table: Friend

```

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

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

```

即使第一个`UPDATE`语句正确，`Friends`表也没有更改。

我们将再次尝试更改两行，这次是在自动提交模式下。

```ruby
#!/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`。

```ruby
db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1"
db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"

```

`UPDATE`语句的第二个不正确。

```ruby
$ ./autocommit2.rb
Exception occurred
no such table: Friend

```

我们收到与上一个示例相同的错误消息。

```ruby
sqlite> SELECT * FROM Friends;
1|Thomas
2|Rebecca
3|Jim
4|Robert
5|Julian

```

但是这一次，第一个`UPDATE`语句被保存。 第二个不是。

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