🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 使用 C# 的 SQLite 事务 > 原文: [http://zetcode.com/db/sqlitecsharp/trans/](http://zetcode.com/db/sqlitecsharp/trans/) 在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们将有一些程序显示如何处理事务。 事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 在 SQLite 中,除`SELECT`以外的任何命令都将启动隐式事务。 同样,在事务中,诸如`CREATE TABL` ...,`VACUUM`和`PRAGMA`之类的命令将在执行之前提交先前的更改。 手动事务以`BEGIN TRANSACTION`语句开始,并以`COMMIT`或`ROLLBACK`语句结束。 SQLite 支持三种非标准的事务级别:`DEFERRED`,`IMMEDIATE`和`EXCLUSIVE`。 除非我们启动自己的事务,否则 SQLite 会自动将每个命令放入其自己的事务中。 请注意,这也可能会受到驱动程序的影响。 SQLite Python 驱动程序默认情况下已关闭自动提交模式,并且第一个 SQL 命令启动新事务。 ```cs using System; using Mono.Data.Sqlite; public class Example { static void Main() { string cs = "URI=file:test.db"; using (SqliteConnection con = new SqliteConnection(cs)) { con.Open(); using (SqliteCommand cmd = con.CreateCommand()) { 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(); } con.Close(); } } } ``` 我们创建一个`Friends`表,并用数据填充它。 我们没有明确地启动事务,也没有调用`commit`或`rollback`方法。 但是数据已写入表中。 这是因为默认情况下,我们在自动提交模式下工作。 在这种模式下,每个 SQL 语句立即生效。 ```cs 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`语句创建表。 ```cs cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"; cmd.ExecuteNonQuery(); cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"; cmd.ExecuteNonQuery(); ... ``` 我们插入两行。 ```cs sqlite> SELECT * FROM Friends; 1|Tom 2|Rebecca 3|Jim 4|Robert 5|Julian ``` `Friends`表已成功创建。 在第二个示例中,我们将使用`BeginTransaction()`方法启动自定义事务。 ```cs using System; using Mono.Data.Sqlite; public class Example { static void Main() { string cs = "URI=file:test.db"; using (SqliteConnection con = new SqliteConnection(cs)) { con.Open(); using(SqliteTransaction tr = con.BeginTransaction()) { using (SqliteCommand 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(); } con.Close(); } } } ``` 所有 SQL 命令组成一个单元。 要么全部保存,要么什么都不保存。 这是事务背后的基本思想。 ```cs using(SqliteTransaction tr = con.BeginTransaction()) ``` `BeginTransaction()`方法启动事务。 ```cs cmd.Transaction = tr; ``` 我们设置`SqliteCommand`在其中执行的事务。 ```cs tr.Commit(); ``` 如果一切正常,我们将整个事务提交到数据库。 如果发生异常,则事务将在后台回滚。 ## 显式回滚调用 现在,我们将显示一个示例,在发生异常的情况下,我们手动回滚事务。 ```cs using System; using Mono.Data.Sqlite; public class Example { static void Main() { string cs = "URI=file:test.db"; SqliteConnection con = null; SqliteTransaction tr = null; SqliteCommand cmd = null; 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 (SqliteException ex) { Console.WriteLine("Error: {0}", ex.ToString()); if (tr != null) { try { tr.Rollback(); } catch (SqliteException ex2) { Console.WriteLine("Transaction rollback failed."); Console.WriteLine("Error: {0}", ex2.ToString()); } finally { tr.Dispose(); } } } finally { if (cmd != null) { cmd.Dispose(); } if (tr != null) { tr.Dispose(); } if (con != null) { try { con.Close(); } catch (SqliteException ex) { Console.WriteLine("Closing connection failed."); Console.WriteLine("Error: {0}", ex.ToString()); } finally { con.Dispose(); } } } } } ``` 我们创建自己的`try`,`catch`,`finally`块,以解决可能的问题。 ```cs } catch (SqliteException ex) { Console.WriteLine("Error: {0}", ex.ToString()); if (tr != null) { try { tr.Rollback(); } catch (SqliteException ex2) { Console.WriteLine("Transaction rollback failed."); Console.WriteLine("Error: {0}", ex2.ToString()); } finally { tr.Dispose(); } } } ``` 在创建`Friends`表的过程中引发异常时,我们将调用`Rollback()`方法。 即使进行回滚,也可能会发生异常。 因此,我们也检查这种情况。 ```cs if (cmd != null) { cmd.Dispose(); } if (tr != null) { tr.Dispose(); } ``` 当一切顺利时,我们将配置资源。 ```cs if (con != null) { try { con.Close(); } catch (SqliteException ex) { Console.WriteLine("Closing connection failed."); Console.WriteLine("Error: {0}", ex.ToString()); } finally { con.Dispose(); } } ``` 关闭连接时,我们可能会收到另一个异常。 我们在这里处理这种情况。 ## 错误 当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。 ```cs using System; using Mono.Data.Sqlite; public class Example { static void Main() { string cs = "URI=file:test.db"; using (SqliteConnection con = new SqliteConnection(cs)) { con.Open(); using(SqliteTransaction tr = con.BeginTransaction()) { using (SqliteCommand cmd = con.CreateCommand()) { 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(); } con.Close(); } } } ``` 在代码示例中,我们要更改两个名称。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。 ```cs cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"; ``` 表名称不正确。 数据库中没有`Friend`表。 ```cs $ mono trans_error.exe Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error no such table: Friend ... ``` 运行示例将显示此错误消息。 事务回滚。 ```cs sqlite> SELECT * FROM Friends; 1|Tom 2|Rebecca 3|Jim 4|Robert 5|Julian 6|Jane ``` `Friends`表中没有发生任何变化。 即使第一个`UPDATE`语句正确。 我们将再次尝试更改两行; 这次不使用`SqliteTransaction`。 ```cs using System; using Mono.Data.Sqlite; public class Example { static void Main() { string cs = "URI=file:test.db"; using (SqliteConnection con = new SqliteConnection(cs)) { con.Open(); using (SqliteCommand cmd = con.CreateCommand()) { cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1"; cmd.ExecuteNonQuery(); cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"; cmd.ExecuteNonQuery(); } con.Close(); } } } ``` 我们尝试更新`Friends`表中的两个名称,汤姆(Tom)到托马斯(Thomas)和罗伯特(Robert)到鲍勃(Bob)。 ```cs cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"; cmd.ExecuteNonQuery(); ``` 此`UPDATE`语句不正确。 ```cs $ mono notrans_error.exe Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error no such table: Friend ``` 我们收到与上一个示例相同的错误消息。 ```cs sqlite> SELECT * FROM Friends; 1|Thomas 2|Rebecca 3|Jim 4|Robert 5|Julian 6|Jane ``` 但是这一次,第一个`UPDATE`语句被保存。 第二个不是。 在 SQLite C# 教程的这一部分中,我们处理了事务。