執行交易

你可以使用表示交易的 sql.Tx, 來執行資料庫交易。除了表示交易特定語意的 CommitRollback 方法外,sql.Tx 還具有所有用於執行常見資料庫操作的方法。若要取得 sql.Tx,請呼叫 DB.BeginDB.BeginTx

一個 資料庫交易 將多個操作分組為較大目標的一部分。所有操作都必須成功,否則沒有任何操作可以成功,而且在任何情況下都必須保留資料的完整性。通常,交易工作流程包括

  1. 開始交易。
  2. 執行一組資料庫操作。
  3. 如果沒有發生錯誤,提交交易以進行資料庫變更。
  4. 如果發生錯誤,則回滾交易以使資料庫保持不變。

sql 套件提供開始和結束交易的方法,以及執行介入資料庫操作的方法。這些方法對應於上述工作流程中的四個步驟。

最佳做法

遵循以下最佳做法,以更好地導覽交易有時需要的複雜語意和連線管理。

範例

以下範例中的程式碼使用交易為專輯建立新的客戶訂單。在此過程中,程式碼將會

  1. 開始交易。
  2. 延後交易的回滾。如果交易成功,它會在函式結束前提交,讓延後的回滾呼叫成為空操作。如果交易失敗,它不會提交,表示函式結束時會呼叫回滾。
  3. 確認客戶訂購的專輯有足夠庫存。
  4. 如果數量足夠,更新庫存數量,減少訂購的專輯數量。
  5. 建立新訂單並為客戶擷取新訂單產生的 ID。
  6. 提交交易並傳回 ID。

此範例使用需要 context.Context 參數的 Tx 方法。這讓函式的執行(包括資料庫操作)可以在執行時間過長或客戶端連線關閉時取消。如需更多資訊,請參閱 取消進行中的操作

// CreateOrder creates an order for an album and returns the new order ID.
func CreateOrder(ctx context.Context, albumID, quantity, custID int) (orderID int64, err error) {

    // Create a helper function for preparing failure results.
    fail := func(err error) (int64, error) {
        return 0, fmt.Errorf("CreateOrder: %v", err)
    }

    // Get a Tx for making transaction requests.
    tx, err := db.BeginTx(ctx, nil)
    if err != nil {
        return fail(err)
    }
    // Defer a rollback in case anything fails.
    defer tx.Rollback()

    // Confirm that album inventory is enough for the order.
    var enough bool
    if err = tx.QueryRowContext(ctx, "SELECT (quantity >= ?) from album where id = ?",
        quantity, albumID).Scan(&enough); err != nil {
        if err == sql.ErrNoRows {
            return fail(fmt.Errorf("no such album"))
        }
        return fail(err)
    }
    if !enough {
        return fail(fmt.Errorf("not enough inventory"))
    }

    // Update the album inventory to remove the quantity in the order.
    _, err = tx.ExecContext(ctx, "UPDATE album SET quantity = quantity - ? WHERE id = ?",
        quantity, albumID)
    if err != nil {
        return fail(err)
    }

    // Create a new row in the album_order table.
    result, err := tx.ExecContext(ctx, "INSERT INTO album_order (album_id, cust_id, quantity, date) VALUES (?, ?, ?, ?)",
        albumID, custID, quantity, time.Now())
    if err != nil {
        return fail(err)
    }
    // Get the ID of the order item just created.
    orderID, err = result.LastInsertId()
    if err != nil {
        return fail(err)
    }

    // Commit the transaction.
    if err = tx.Commit(); err != nil {
        return fail(err)
    }

    // Return the order ID.
    return orderID, nil
}