使用通用框架在 Go 中建立健全的 SQL 事務執行
在 Go 中使用 SQL 資料庫時,確保原子性並在多步驟事務期間管理回滾可能具有挑戰性。在本文中,我將指導您建立一個健全、可重複使用且可測試的框架,用於在 Go 中執行 SQL 事務,並使用泛型來實現靈活性。
我們將建立一個 SqlWriteExec 實用程序,用於在事務中執行多個相關資料庫操作。它支援無狀態和有狀態操作,支援複雜的工作流程,例如插入相關實體,同時無縫管理依賴關係。
為什麼我們需要 SQL 交易框架?
在實際應用中,資料庫操作很少是孤立的。考慮以下場景:
插入使用者並自動更新其庫存。
建立訂單並處理付款,確保一致性。
由於涉及多個步驟,在故障期間管理回滾對於確保資料完整性至關重要。
在 Txn 管理中使用 go。
如果您正在編寫資料庫交易,那麼在編寫核心邏輯之前您可能需要考慮幾個樣板。雖然這個 txn 管理是由 java 中的 spring boot 管理的,並且在用 java 編寫程式碼時你從來沒有太在意這些,但在 golang 中卻不是這樣。下面提供了一個簡單的範例
func basicTxn(db *sql.DB) error { // start a transaction tx, err := db.Begin() if err != nil { return err } defer func() { if r := recover(); r != nil { tx.Rollback() } else if err != nil { tx.Rollback() } else { tx.Commit() } }() // insert data into the orders table _, err = tx.Exec("INSERT INTO orders (id, customer_name, order_date) VALUES (1, 'John Doe', '2022-01-01')") if err != nil { return err } return nil }
我們不能指望為每個函數重複回滾/提交程式碼。這裡我們有兩個選擇,要么創建一個類,該類將提供一個函數作為返回類型,該類在defer 中執行時將提交/回滾txn,要么創建一個包裝類,它將所有txn 函數包裝在一起並一次性執行。
我選擇了後面的選擇,程式碼的變更如下所示。
func TestSqlWriteExec_CreateOrderTxn(t *testing.T) { db := setupDatabase() // create a new SQL Write Executor err := dbutils.NewSqlTxnExec[OrderRequest, OrderProcessingResponse](context.TODO(), db, nil, &OrderRequest{CustomerName: "CustomerA", ProductID: 1, Quantity: 10}). StatefulExec(InsertOrder). StatefulExec(UpdateInventory). StatefulExec(InsertShipment). Commit() // check if the transaction was committed successfully if err != nil { t.Fatal(err) return } verifyTransactionSuccessful(t, db) t.Cleanup( func() { cleanup(db) db.Close() }, ) }
func InsertOrder(ctx context.Context, txn *sql.Tx, order *OrderRequest, orderProcessing *OrderProcessingResponse) error { // Insert Order result, err := txn.Exec("INSERT INTO orders (customer_name, product_id, quantity) VALUES (, , )", order.CustomerName, order.ProductID, order.Quantity) if err != nil { return err } // Get the inserted Order ID orderProcessing.OrderID, err = result.LastInsertId() return err } func UpdateInventory(ctx context.Context, txn *sql.Tx, order *OrderRequest, orderProcessing *OrderProcessingResponse) error { // Update Inventory if it exists and the quantity is greater than the quantity check if it exists result, err := txn.Exec("UPDATE inventory SET product_quantity = product_quantity - WHERE id = AND product_quantity >= ", order.Quantity, order.ProductID) if err != nil { return err } // Get the number of rows affected rowsAffected, err := result.RowsAffected() if rowsAffected == 0 { return errors.New("Insufficient inventory") } return err } func InsertShipment(ctx context.Context, txn *sql.Tx, order *OrderRequest, orderProcessing *OrderProcessingResponse) error { // Insert Shipment result, err := txn.Exec("INSERT INTO shipping_info (customer_name, shipping_address) VALUES (, 'Shipping Address')", order.CustomerName) if err != nil { return err } // Get the inserted Shipping ID orderProcessing.ShippingID, err = result.LastInsertId() return err }
這段程式碼將會更加精確和簡潔。
核心邏輯是如何實現的
這個想法是將 txn 隔離到單一 go 結構,以便它可以接受多個 txn。我所說的 txn 是指將使用我們為類別創建的 txn 執行操作的函數。
type TxnFn[T any] func(ctx context.Context, txn *sql.Tx, processingReq *T) error type StatefulTxnFn[T any, R any] func(ctx context.Context, txn *sql.Tx, processingReq *T, processedRes *R) error
這兩個是函數類型,它們將接受 txn 來處理某些內容。現在在資料層中實作建立一個像這樣的函數並將其傳遞給執行器類,該執行器類別負責注入參數並執行該函數。
// SQL Write Executor is responsible when executing write operations // For dependent writes you may need to add the dependent data to processReq and proceed to the next function call type SqlTxnExec[T any, R any] struct { db *sql.DB txn *sql.Tx txnFns []TxnFn[T] statefulTxnFns []StatefulTxnFn[T, R] processingReq *T processedRes *R ctx context.Context err error }
這是我們儲存所有 txn_fn 詳細資訊的地方,我們將使用 Commit() 方法來嘗試提交 txn。
func (s *SqlTxnExec[T, R]) Commit() (err error) { defer func() { if p := recover(); p != nil { s.txn.Rollback() panic(p) } else if err != nil { err = errors.Join(err, s.txn.Rollback()) } else { err = errors.Join(err, s.txn.Commit()) } return }() for _, writeFn := range s.txnFns { if err = writeFn(s.ctx, s.txn, s.processingReq); err != nil { return } } for _, statefulWriteFn := range s.statefulTxnFns { if err = statefulWriteFn(s.ctx, s.txn, s.processingReq, s.processedRes); err != nil { return } } return }
您可以在儲存庫中找到更多範例和測試 -
https://github.com/mahadev-k/go-utils/tree/main/examples
雖然現在我們偏向分散式系統和共識協議,但我們仍然使用sql並且它仍然存在。
如果有人願意貢獻並在此基礎上繼續發展,請告訴我! !
感謝您閱讀本文! !
https://in.linkedin.com/in/mahadev-k-934520223
https://x.com/mahadev_k_
以上是使用通用框架在 Go 中建立健全的 SQL 事務執行的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Go語言在構建高效且可擴展的系統中表現出色,其優勢包括:1.高性能:編譯成機器碼,運行速度快;2.並發編程:通過goroutines和channels簡化多任務處理;3.簡潔性:語法簡潔,降低學習和維護成本;4.跨平台:支持跨平台編譯,方便部署。

Golang在並發性上優於C ,而C 在原始速度上優於Golang。 1)Golang通過goroutine和channel實現高效並發,適合處理大量並發任務。 2)C 通過編譯器優化和標準庫,提供接近硬件的高性能,適合需要極致優化的應用。

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

C 更適合需要直接控制硬件資源和高性能優化的場景,而Golang更適合需要快速開發和高並發處理的場景。 1.C 的優勢在於其接近硬件的特性和高度的優化能力,適合遊戲開發等高性能需求。 2.Golang的優勢在於其簡潔的語法和天然的並發支持,適合高並發服務開發。

goimpactsdevelopmentpositationality throughspeed,效率和模擬性。 1)速度:gocompilesquicklyandrunseff,IdealforlargeProjects.2)效率:效率:ITScomprehenSevestAndardArdardArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增強的Depleflovelmentimency.3)簡單性。

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。
