Table of Contents

SQL - Transaction

當多個 SQL 操作必須要嘛全成功、要嘛全失敗時,需要 transaction(資料庫交易)。ZapLib 的 SQL 在建構時打開 transaction: true 即啟用。

Namespace

using ZapLib;

Enable Transaction

任何 SQL 建構子的最後一個參數都是 transaction

SQL db = new SQL("DefaultConn", transaction: true);

啟用後:

  • Connect() 開連線時會自動 BeginTransaction()
  • 每個 QuickXxx 方法成功完成會自動 Tran.Commit()
  • 任何一步丟出 exception 會自動 Tran.Rollback()

Atomic Single Operation

⚠️ QuickXxx 系列方法每次呼叫都會自動開關連線並 commit/rollback。意思是「多筆 QuickXxx 仍各自為政」、不是同一個 transaction

SQL db = new SQL("DefaultConn", transaction: true);

// ❌ 這兩個 INSERT 不在同一個 transaction!
db.QuickQuery<dynamic>("INSERT INTO A (...) VALUES (...)", ...);
db.QuickQuery<dynamic>("INSERT INTO B (...) VALUES (...)", ...);  // 即使這句失敗,A 也已 commit

Multi-Statement Transaction — Manual Mode

要讓多個 SQL 在同一個 transaction 中,必須走手動模式:

SQL db = new SQL("DefaultConn", transaction: true);
db.Connect();

if (db.IsConn)
{
    try
    {
        // 第一個操作
        db.Cmd.CommandText = "INSERT INTO Orders (user_id, total) VALUES (@uid, @total)";
        db.Cmd.Parameters.Clear();
        db.Cmd.Parameters.AddWithValue("@uid", 100);
        db.Cmd.Parameters.AddWithValue("@total", 1500);
        db.Cmd.ExecuteNonQuery();

        // 第二個操作(同一個 transaction)
        db.Cmd.CommandText = "UPDATE Inventory SET stock = stock - 1 WHERE product_id = @pid";
        db.Cmd.Parameters.Clear();
        db.Cmd.Parameters.AddWithValue("@pid", 200);
        db.Cmd.ExecuteNonQuery();

        // 兩個都成功 → commit
        db.Tran.Commit();
    }
    catch (Exception ex)
    {
        // 任何一步失敗 → rollback
        db.Tran.Rollback();
        Console.WriteLine("交易失敗:" + ex.Message);
    }
    finally
    {
        db.Close();
    }
}

Helper Pattern

把上面樣板包成一個 helper:

public static bool RunInTransaction(string connName, Action<SQL> work)
{
    SQL db = new SQL(connName, transaction: true);
    db.Connect();

    if (!db.IsConn) return false;

    try
    {
        work(db);
        db.Tran.Commit();
        return true;
    }
    catch (Exception ex)
    {
        db.Tran.Rollback();
        new MyLog().Write("Transaction failed: " + ex.ToString());
        return false;
    }
    finally
    {
        db.Close();
    }
}

// 使用
bool ok = RunInTransaction("DefaultConn", db =>
{
    db.Cmd.CommandText = "INSERT INTO Orders ...";
    db.Cmd.ExecuteNonQuery();

    db.Cmd.CommandText = "UPDATE Inventory ...";
    db.Cmd.ExecuteNonQuery();
});

Bulk Copy with Transaction

QuickBulkCopytransaction: true 時,會在成功後自動 commit、失敗時自動 rollback:

SQL db = new SQL("DefaultConn", transaction: true);

DataTable dt = BuildDataTable(largeData);
bool ok = db.QuickBulkCopy(dt, "TargetTable");

if (!ok)
{
    Console.WriteLine("批次寫入失敗,已 rollback");
}

詳見 Bulk Copy

Inspecting the Transaction Object

需要直接操作 SqlTransaction

SQL db = new SQL("DefaultConn", transaction: true);
db.Connect();

SqlTransaction tran = db.Tran;
// 設定 IsolationLevel? 自訂 SavePoint?
tran.Save("BeforeCriticalStep");

// ... 跑 SQL ...

// 部分回滾到 save point
tran.Rollback("BeforeCriticalStep");

See Also