尽管我不回滚事务,但 SQLite 事务未成功?

发布于 2024-12-03 03:50:21 字数 1414 浏览 3 评论 0原文

创建连接+事务:

public SQLiteTransaction BeginTransaction()
{
            var con = new SQLiteConnection(@"Data Source=A:\TransactionScopeTest\TransactionTest.db;Foreign Keys=ON");
            con.Open();
            var trans = con.BeginTransaction();
            return trans;
}

使用相同的主键值执行 2 个 sqlite 插入以引发异常

[TestMethod]
public void TestMethod1()
{
    using (var trans = BeginTransaction())
    {
        try
        {
            SQLiteConnection con = trans.Connection;

            SQLiteCommand cmd1 = con.CreateCommand();
            cmd1.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd1.ExecuteNonQuery();

            SQLiteCommand cmd2 = con.CreateCommand();
            cmd2.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd2.ExecuteNonQuery();                   

            trans.Commit();
        }
        catch (Exception)
        {
            trans.Rollback();
            throw;
        }
    }
}

当我使用 SQLite 时,其最佳实践是对每个执行的 sql 命令使用 SQLiteTransaction 类。 来自事务的连接需要在数据提供者方法之间共享。

我现在问您多个问题:

1.) 当由于插入相同的主键“John”而发生 SQLiteException 时,不会插入任何“John”值。没关系,因为我使用了事务并且必须执行 .Commit()。让我困扰的是,为什么我在 catch 块中使用 trans.Rollback() 没有任何区别。

2.) 我正在使用“using(resource)”语句,那么如果事务成功/提交到连接状态会发生什么?会被关闭吗?只是担心我不使用 `using(var trans = new SQLiteTransaction()){...}

Create the Connection + Transaction:

public SQLiteTransaction BeginTransaction()
{
            var con = new SQLiteConnection(@"Data Source=A:\TransactionScopeTest\TransactionTest.db;Foreign Keys=ON");
            con.Open();
            var trans = con.BeginTransaction();
            return trans;
}

Do 2 sqlite inserts with same Primary Key value to raise an exception

[TestMethod]
public void TestMethod1()
{
    using (var trans = BeginTransaction())
    {
        try
        {
            SQLiteConnection con = trans.Connection;

            SQLiteCommand cmd1 = con.CreateCommand();
            cmd1.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd1.ExecuteNonQuery();

            SQLiteCommand cmd2 = con.CreateCommand();
            cmd2.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd2.ExecuteNonQuery();                   

            trans.Commit();
        }
        catch (Exception)
        {
            trans.Rollback();
            throw;
        }
    }
}

As I use SQLite its best practice to use the SQLiteTransaction class for every executed sql command.
The connection from the transaction needs to be shared among the dataprovider methods.

I am asking YOU now multiple questions:

1.) When a SQLiteException occurs because of inserting same primary keys "John" there is not inserted any of the "John" values. That is ok because I used a transaction and the .Commit() must be executed. What bothers me is WHY does it not make any difference wether OR NOT I use trans.Rollback() in the catch-block.

2.) I am using the "using(resource)"-statement so what will happen if the transaction succeeds/commits to the state of the connection ? Will it be closed? Just concern that I do not do use the `using(var trans = new SQLiteTransaction()){...}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愛放△進行李 2024-12-10 03:50:21

回答你的问题:

  1. 交易必须像丹尼尔所说的那样明确提交。在意外错误情况下,我宁愿我的数据保持原样,而不是处于半提交状态,这是事务的重点。在这种情况下,catch 块可用于重试具有不同参数等的操作。在我的工作中的许多情况下,如果事务在没有提交的情况下到达 using 语句的末尾,它将回滚它,而无需我编写显式的 try/catch。请记住,在几乎所有异常情况下,即使您没有捕获异常,using 块中的对象仍将被释放。 (我喜欢这种方法,因为代码更干净,没有到处 try/catch - 我只在可以做出相应反应时才使用 try/catch)
  2. using 语句很好。如果事务已提交,则不会回滚任何内容。如果事务尚未提交,则事务将回滚。但请记住,处置事务对象不会显式关闭底层数据库连接。

不过,我注意到的一件事是,您创建的命令对象与事务关联。如果要针对 SQL Server 或 Oracle 执行此代码,则会抛出异常,指出所有命令必须分配活动事务(如果有)。

要将命令与事务关联起来,在创建每个新命令对象后,您将需要以下代码:

cmd.Transaction = trans;

通常,我的数据库代码遵循以下格式:

using (SqlConnection connection = new SqlConnection("...")) {
  connection.Open();
  using (SqlTransaction transaction = connection.BeginTransaction())
  using (SqlCommand command = connection.CreateCommand()) {
    command.Transaction = transaction;
    command.CommandText = "INSERT INTO ...";
    // add parameters...
    command.ExecuteNonQuery();
    transaction.Commit();
  }
  // Reference to question 1: At this point in the code, assuming NO unhandled
  // exceptions occurred, the connection object is still open and can be used.
  // for example:
  using (SqlCommand command = connection.CreateCommand()) {
    command.CommandText = "SELECT ...";
    using (SqlDataReader reader = command.ExecuteReader()) {
      while (reader.Read()) {
        // do awesome processing here.
      }
    }
  }
}

上面的连接流程将确保所有与连接、事务相关的资源,并且命令对象在发生异常时被清理。如果抛出异常,则错误位于抛出异常的行,而不是捕获并再次抛出异常的 catch 块。此外,事务将被回滚,并且底层数据库连接将被关闭(或返回到池中,如果存在的话)。

请记住,如果某个东西有 Dispose() 方法并实现了 IDisposable 接口,最好将其包装在 using 语句中,因为即使调用 Dispose( ) 现在什么都不做,不能保证将来会这样。

To answer your questions:

  1. Transactions must be committed explicitly as Daniel said. In an unexpected error condition I would rather my data be left as is and not in a half-committed state, which is the point of a transaction. In this case, the catch block could be used to retry an operation with different parameters and such. In many cases with my work, if the transaction hits the end of a using statement without a commit it will roll it back without me coding an explicit try/catch. Remember, in almost all exception cases the objects in a using block will still be disposed, even if you don't catch the exception. (I like this method because the code is cleaner without try/catches everywhere - I only use try/catch when I can react accordingly)
  2. The using statement is fine. If the transaction has been committed, nothing will be rolled back. If the transaction has not been committed the transaction will be rolled back. Keep in mind though, disposing a transaction object will not explicitly close the underlying database connection.

One thing I noticed, though, is that your command objects you've created are not associated with the transaction. If this code were to be executed against SQL server or Oracle an exception would be thrown stating that all commands must be assigned the active transaction (if there is one).

To associate the command with the transaction you'll need the following piece of code after each new command object created:

cmd.Transaction = trans;

Typically my database code follows the format of:

using (SqlConnection connection = new SqlConnection("...")) {
  connection.Open();
  using (SqlTransaction transaction = connection.BeginTransaction())
  using (SqlCommand command = connection.CreateCommand()) {
    command.Transaction = transaction;
    command.CommandText = "INSERT INTO ...";
    // add parameters...
    command.ExecuteNonQuery();
    transaction.Commit();
  }
  // Reference to question 1: At this point in the code, assuming NO unhandled
  // exceptions occurred, the connection object is still open and can be used.
  // for example:
  using (SqlCommand command = connection.CreateCommand()) {
    command.CommandText = "SELECT ...";
    using (SqlDataReader reader = command.ExecuteReader()) {
      while (reader.Read()) {
        // do awesome processing here.
      }
    }
  }
}

This flow of the connections above will ensure that all related resources with the connection, the transaction, and the command object are cleaned up in the event of an exception. If an exception is thrown, the error is on the line that threw it, not the catch block that caught and threw it again. In addition, the transaction would be rolled back and the underlying database connection would be closed (or returned to the pool, if one existed).

Remember, if something has a Dispose() method and implements the IDisposable interface, it is best to wrap it in a using statement, because even if calling Dispose() does nothing now, there is no guarantee it will be that way in the future.

蓝咒 2024-12-10 03:50:21

因为事务存在隐式回滚。提交必须是明确的。

连接最终将由运行时关闭。

Because there is an implicit rollback with transactions. Commits have to be explicit.

The connection will be closed eventually by the runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文