GO에서 SQL 데이터베이스와 작업 할 때 멀티 단계 트랜잭션 중에 원자력과 롤백 관리가 어려울 수 있습니다. 이 기사에서는 유연성을 위해 제네릭을 사용하여 GO에서 SQL 트랜잭션을 실행하기위한 강력하고 재사용 가능하며 테스트 가능한 프레임 워크를 작성하여 안내합니다.
SQL 트랜잭션을위한 프레임 워크가 필요한 이유는 무엇입니까?
실제 응용 프로그램에서 데이터베이스 작업은 거의 격리되지 않습니다. 이 시나리오를 고려하십시오 :
사용자를 삽입하고 원자 적으로 인벤토리를 업데이트합니다.
TXN 관리에서 GO와 협력합니다.
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 }
func basictxn (db *sql.db) 오류 { // 트랜잭션을 시작합니다 tx, err : = db.begin () err! = nil {인 경우 반환 오류 } 연기 func () { r : = 복구 (); r! = nil { tx.rollback () } else if err! = nil { tx.rollback () } 또 다른 { tx.commit () } } () // 주문 테이블에 데이터를 삽입하십시오 _, err = tx.exec ( "주문 (id, customer_name, order_date) 값 (1, 'John Doe', '2022-01-01')")에 삽입 err! = nil {인 경우 반환 오류 } 반환 nil }
우리는 모든 함수에 대해 롤백/커밋 코드를 반복 할 것으로 기대할 수 없습니다. 여기에는 DEFER에서 실행될 때 TXN이 Commit/Rollback TXN을 커밋/롤백하거나 한 번에 모든 TXN Funcs를 함께 랩핑하고 한 번에 실행할 래퍼 클래스를 생성하는 반환 유형으로 기능을 제공하는 클래스를 작성하는 두 가지 옵션이 있습니다.
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 }
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 ($1, $2, $3)", 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 - $1 WHERE id = $2 AND product_quantity >= $1", 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 ($1, 'Shipping Address')", order.CustomerName) if err != nil { return err } // Get the inserted Shipping ID orderProcessing.ShippingID, err = result.LastInsertId() return err }
func insertorder (ctx context.context, txn *sql.tx, Order *OrderRequest, OrderProcessing *OrderProcessingResponse) 오류 { // 순서 삽입 result, err : = tx.exec ( "주문 (customer_name, product_id, 수량) 값 ($ 1, $ 2, $ 3) 값에 삽입 err! = nil {인 경우 반환 오류 } // 삽입 된 주문 ID를 가져옵니다 OrderProcessing.OrderId, err = result.lastinsertid () 반환 오류 } func updateInventory (ctx context.context, txn *sql.tx, Order *OrderRequest, OrderProcessing *OrderProcessingResponse) 오류 { // 재고가 존재하고 수량이 존재하는지 확인하는 경우 수량이 더 큰 경우 업데이트 결과, err : = tx.exec ( "인벤토리 업데이트 세트 product_quantity = product_quantity - $ 1 여기서 id = $ 2 및 product_quantity> = $ 1", Order.Quantity, Order.ProductId) err! = nil {인 경우 반환 오류 } // 영향을받는 행 수를 얻습니다 RowsAffrected, err : = result.rowsaffrected () RowsAffed가있는 경우 == 0 { 리턴 오류 .New ( "불충분 한 재고") } 반환 오류 } func insertshipment (ctx context.context, txn *sql.tx, Order *OrderRequest, OrderProcessing *OrderProcessingResponse) 오류 { // 배송 삽입 result, err : = tx.exec ( "shippling_info (customer_name, shippling_address) 값 ($ 1, '배송 주소')", Order.CustomerName)에 삽입 err! = nil {인 경우 반환 오류 } // 삽입 된 배송 ID를 가져옵니다 OrderProcessing.shampingid, err = result.lastinsertid () 반환 오류 }
핵심 논리가 구현되는 방법
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 }
typt txnfn [t any] func (ctx context.context, txn *sql.tx, processingReq *t) 오류
statefultxnfn [t any, r] func (ctx context.context, txn *sql.tx, processingreq *t, processedres *r) 오류 유형
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 }
// SQL Write Executor는 쓰기 작업을 실행할 때 책임이 있습니다.
// 종속 쓰기의 경우 ProcessReq에 종속 데이터를 추가하고 다음 함수 호출로 진행해야 할 수도 있습니다.
sqltxnexec [t any, r] struct {유형
db *sql.db
txn *sql.tx
txnfns [] txnfn [t]
statefultxnfns [] statefultxnfn [t, r]
ProcessingReq *t
ProcessedRes *r
ctx context.context
오류 오류
}
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 }
func (sqltxnexec [t, r]) commit () (err error) {
연기 func () {
p : = revery (); p! = nil {
s.txn.rollback ()
공황 (P)
} else if err! = nil {
err = errors.join (err, s.txn.rollback ())
} 또 다른 {
err = errors.join (err, s.txn.commit ())
}
반품
} ()
_, writefn : = 범위 s.txnfns {
if err = writefn (s.ctx, s.txn, s.processingreq); err! = nil {
반품
}
}
for _, statefulwritefn : = 범위 s.statefultxnfns {
err = statefulwritefn (s.ctx, s.txn, s.processingreq, s.processedres); err! = nil {
반품
}
}
반품
}
https://github.com/mahadev-k/go-utils/tree/main/examples ]=&&]
요즘 분산 시스템과 합의 프로토콜에 편향되지만 여전히 SQL을 사용하고 여전히 존재합니다.
누군가 가이 위에 기여하고 구축하기를 원한다면 알려주세요 !!
지금까지 읽어 주셔서 감사합니다 !!
https://in.linkedin.com/in/mahadev-k-9345202222222223=&&]
https://x.com/mahadev_k_=)
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3