# 使用 Visual Basic 的 SQLite 事务

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

在本章中，我们将处理事务。 首先，我们提供一些基本定义。 然后，我们将有一些程序显示如何处理事务。

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库，也可以全部回滚。

在 SQLite 中，除`SELECT`以外的任何命令都将启动隐式事务。 同样，在事务中，诸如`CREATE TABLE` ...，`VACUUM`和`PRAGMA`之类的命令将在执行之前提交先前的更改。

手动事务以`BEGIN TRANSACTION`语句开始，并以`COMMIT`或`ROLLBACK`语句结束。

SQLite 支持三种非标准事务级别：`DEFERRED`，`IMMEDIATE`和`EXCLUSIVE`。 除非我们启动自己的事务，否则 SQLite 会自动将每个命令放入其自己的事务中。 请注意，这也可能会受到驱动程序的影响。 SQLite Python 驱动程序默认情况下已关闭自动提交模式，并且第一个 SQL 命令启动新事务。

```vb
Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "DROP TABLE IF EXISTS Friends"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                                    & "Name TEXT)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
                cmd.ExecuteNonQuery()

            End Using

            con.Close()

        End Using

    End Sub

End Module

```

我们创建一个`Friends`表并用数据填充它。 我们没有明确地启动事务，也没有调用`commit`或`rollback`方法。 但是数据已写入表中。 这是因为默认情况下，我们在自动提交模式下工作。 在这种模式下，每个 SQL 语句立即生效。

```vb
cmd.CommandText = "DROP TABLE IF EXISTS Friends"
cmd.ExecuteNonQuery()
cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                    & "Name TEXT)"
cmd.ExecuteNonQuery()

```

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

```vb
cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
cmd.ExecuteNonQuery()
cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
cmd.ExecuteNonQuery()
...

```

我们插入两行。

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

```

`Friends`表已成功创建。

在第二个示例中，我们将使用`BeginTransaction()`方法启动自定义事务。

```vb
Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim tr As SqliteTransaction = con.BeginTransaction()

            Using tr 

                Using cmd As New SqliteCommand(con)

                    cmd.Transaction = tr
                    cmd.CommandText = "DROP TABLE IF EXISTS Friends"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                                        & "Name TEXT)"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')"
                    cmd.ExecuteNonQuery()
                End Using

                tr.Commit()
            End Using

            con.Close()
        End Using

    End Sub

End Module

```

所有 SQL 命令组成一个单元。 要么全部保存，要么什么都不保存。 这是事务背后的基本思想。

```vb
Dim tr As SqliteTransaction = con.BeginTransaction()

```

`BeginTransaction()`方法启动事务。

```vb
cmd.Transaction = tr

```

我们设置`SqliteCommand`在其中执行的事务。

```vb
tr.Commit()

```

如果一切正常，我们将整个事务提交到数据库。 如果发生异常，则事务将在后台回滚。

## 显式回滚调用

现在，我们将显示一个示例，在发生异常的情况下，我们手动回滚事务。

```vb
Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Dim con As SqliteConnection     
        Dim tr As SqliteTransaction
        Dim cmd As SqliteCommand

        Try
            con = New SqliteConnection(cs)

            con.Open()

            tr = con.BeginTransaction()
            cmd = con.CreateCommand()

            cmd.Transaction = tr
            cmd.CommandText = "DROP TABLE IF EXISTS Friends"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _
                              &  "Name TEXT)"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')"
            cmd.ExecuteNonQuery()

            tr.Commit()

        Catch ex As SqliteException

            Console.WriteLine("Error: {0}",  ex.ToString())

            If tr IsNot Nothing
                Try                 
                    tr.Rollback()                    
                Catch ex2 As SqliteException
                    Console.WriteLine("Transaction rollback failed.")
                    Console.WriteLine("Error: {0}",  ex2.ToString())
                Finally                
                    tr.Dispose()
                End Try
            End If
        Finally 
            If cmd IsNot Nothing
                cmd.Dispose()
            End If

            If tr IsNot Nothing
                tr.Dispose()
            End If

            If con IsNot Nothing
                Try                 
                    con.Close()
                Catch ex As SqliteException                
                    Console.WriteLine("Closing connection failed.")
                    Console.WriteLine("Error: {0}",  ex.ToString())
                Finally                
                    con.Dispose()
                End Try
            End If
        End Try    

    End Sub

End Module

```

我们创建自己的`Try`，`Catch`和`Finally`块，在其中处理可能的问题。

```vb
Catch ex As SqliteException

    Console.WriteLine("Error: {0}",  ex.ToString())

    If tr IsNot Nothing
        Try                 
            tr.Rollback()                    
        Catch ex2 As SqliteException
            Console.WriteLine("Transaction rollback failed.")
            Console.WriteLine("Error: {0}",  ex2.ToString())
        Finally                
            tr.Dispose()
        End Try
    End If

```

在创建`Friends`表的过程中引发异常时，我们将调用`Rollback()`方法。 回滚事务也可能失败； 我们检查这种情况。

```vb
If cmd IsNot Nothing
    cmd.Dispose()
End If

If tr IsNot Nothing
    tr.Dispose()
End If

```

当一切顺利时，我们将配置资源。

```vb
If con IsNot Nothing
    Try                 
        con.Close()
    Catch ex As SqliteException                
        Console.WriteLine("Closing connection failed.")
        Console.WriteLine("Error: {0}",  ex.ToString())
    Finally                
        con.Dispose()
    End Try
End If

```

关闭连接时，我们可能会收到另一个异常。 我们在这里处理这种情况。

## 错误

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

```vb
Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim tr As SqliteTransaction = con.BeginTransaction()

            Using tr 

                Using cmd As New SqliteCommand(con)

                    cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                    cmd.Transaction = tr
                    cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1"
                    cmd.ExecuteNonQuery()

                    cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
                    cmd.ExecuteNonQuery()

                    tr.Commit()
                End Using
            End Using
            con.Close()
        End Using

    End Sub

End Module

```

在代码示例中，我们要更改两个名称。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此，该事务将回滚。

```vb
cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
cmd.ExecuteNonQuery()

```

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

```vb
$ mono error.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend
...

```

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

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

```

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

我们将再次尝试更改两行； 这次不使用`SqliteTransaction`。

```vb
Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1"
                cmd.ExecuteNonQuery()

                cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
                cmd.ExecuteNonQuery()

            End Using

            con.Close()
        End Using

    End Sub

End Module

```

我们尝试更新`Friends`表中的两个名称，汤姆（Tom）到托马斯（Thomas）和罗伯特（Robert）到鲍勃（Bob）。

```vb
cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
cmd.ExecuteNonQuery()

```

此`UPDATE`语句不正确。

```vb
$ mono error2.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend
...

```

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

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

```

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

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