„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Erstellen einer robusten SQL -Transaktionsausführung in Go mit einem generischen Framework

Erstellen einer robusten SQL -Transaktionsausführung in Go mit einem generischen Framework

Gepostet am 2025-03-23
Durchsuche:954

Building Robust SQL Transaction Execution in Go with a Generic Framework

Bei der Arbeit mit SQL-Datenbanken in GO können die Gewährleistung von Atomizität und Verwaltung von Rollbacks während mehrstufiger Transaktionen eine Herausforderung sein. In diesem Artikel werde ich Sie durch das Erstellen eines robusten, wiederverwendbaren und überprüfbaren Frameworks zur Ausführung von SQL -Transaktionen in Go unter Verwendung von Generika für Flexibilität führen.

Wir erstellen ein SQLWriteExec -Dienstprogramm zur Ausführung mehrerer abhängiger Datenbankvorgänge in einer Transaktion. Es unterstützt sowohl staatenlose als auch staatliche Operationen und ermöglicht ausgefeilte Workflows wie Einfügen verwandter Entitäten, während Abhängigkeiten nahtlos verwaltet werden.

Warum brauchen wir ein Framework für SQL -Transaktionen?

In realen Anwendungen sind Datenbankvorgänge selten isoliert. Betrachten Sie diese Szenarien:

Einfügen eines Benutzers und aktualisiert atomisch.
.
Erstellen einer Bestellung und Bearbeitung ihrer Zahlung, Gewährleistung der Konsistenz.

Mit mehreren Schritten wird das Verwalten von Rollbacks während des Fehlers entscheidend, um die Datenintegrität zu gewährleisten.

Arbeiten mit Go im TXN -Management.


Wenn Sie eine Datenbank schreiben, gibt es möglicherweise mehrere Kesselplatten, die Sie möglicherweise berücksichtigen müssen, bevor Sie die Kernlogik schreiben. Während dieses TXN -Management von Spring Boot in Java verwaltet wird und Sie sich beim Schreiben von Code in Java nie viel darum gekümmert haben, ist dies in Golang nicht der Fall. Ein einfaches Beispiel wird unten angegeben

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) Fehler { // eine Transaktion starten tx, err: = db.begin () Wenn er! = nil { Return err zurück } Defer func () { wenn r: = recover (); r! = nil { tx.rollback () } else wenn err! = nil { tx.rollback () } anders { tx.commit () } } ()) // Daten in die Bestellentabelle einfügen _, err = tx.exec ("In Bestellungen einfügen (ID, Customer_Name, Order_date) Werte (1, 'John Doe', '2022-01-01')"))) Wenn er! = nil { Return err zurück } Null zurückkehren }

Wir können nicht erwarten, den Rollback/Commit -Code für jede Funktion zu wiederholen. Wir haben hier zwei Optionen, die entweder eine Klasse erstellen, die eine Funktion als Rückgabetyp liefert, die bei der Ausführung im Auflauf TXN verpflichtet/rollback txn erstellt oder eine Wrapper -Klasse erstellt, die alle TXN -Funktionen zusammenwickelt und in einem Go.

.

Ich habe mich mit der späteren Wahl und der Änderung des Codes nachstehend gesehen.
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 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 testsqlwriteExec_createordertxn (t *testing.t) { DB: = setupdatabase () // Erstellen Sie einen neuen SQL -Writing Executor err: = dbutils.newsqltxnexec [orderRequest, orderprocessingResponse] (context.todo (), db, nil & orderRequest {Customername: "Customera", ProductID: 1, Menge: 10}). Statefulexec (InsertOrder). StateFullexec (UpdateInventory). StateFulexec (Einfügung). Begehen() // Überprüfen Sie, ob die Transaktion erfolgreich begangen wurde Wenn er! = nil { t.fatal (arr) zurückkehren } verifyTransactionscessful (t, db) t.cleanup ( func () { Reinigung (DB) db.close () }, ) }

func InsertOrder (ctx context.context, txn * // Bestellung einfügen Ergebnis, err: = txn.exec ("In Bestellungen einfügen (Customer_Name, product_id, Menge) Werte ($ 1, $ 2, $ 3)", order.customername, order.productid, order.quantity) Wenn er! = nil { Return err zurück } // Die eingefügte Bestell -ID erhalten orderprocessing.orderId, err = result.lastinSertId () Return err zurück } func updateInventory (ctx context.context, txn * // Inventar aktualisieren, wenn es vorhanden ist und die Menge größer ist als die Menge überprüfen, ob es vorhanden ist Ergebnis, err: = txn.exec ("Inventar set product_quantity = product_quantity - $ 1 wob Wenn er! = nil { Return err zurück } // Die Anzahl der betroffenen Zeilen erhalten RowsAffected, err: = result.rowsafflected () Wenn rowsaffcted == 0 { Rückgabefehler.New ("Unzureichend Inventar") } Return err zurück } Func Insertship (CTX Context.Context, Txn * // Sendung einfügen Ergebnis, err: = txn.exec ("In Shipping_Info einfügen (Customer_Name, Shipping_address) Werte ($ 1, 'Versandadresse')", order.customername) Wenn er! = nil { Return err zurück } // Holen Sie sich die eingeführte Versand -ID orderprocessing.shippingid, err = result.lastinSertId () Return err zurück }

Dieser Code wird sehr viel präziser und prägnanter sein.


Wie die Kernlogik implementiert wird

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


type txnfn [t irgendein] func (ctx context.context, txn *sql.tx, procesalreq *t) Fehler Typ StateFullTxnfn [t beliebig, r jeder] func (ctx context.context, 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
}


// sql write Executor ist bei der Ausführung von Schreibvorgängen verantwortlich // Für abhängige Schreibvorgänge müssen Sie möglicherweise die abhängigen Daten zu procesReq hinzufügen und mit dem nächsten Funktionsaufruf fortfahren Geben Sie SQLTXNexec [t an, r jeder] Struktur { db *sql.db txn *sql.tx txnfns [] txnfn [t] statefultxnfns [] statefulTxnfn [t, r] procesreq *t Processedres *r ctx context.context Fehler Fehler }

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
}


func (s * Defer func () { wenn p: = recover (); p! = nil { S.Txn.Rollback () Panik (p) } else wenn err! = nil { err = errors.join (err, S.Txn.rollback ()) } anders { err = errors.join (err, S.Txn.Commit ()) } zurückkehren } ()) Für _, writeFn: = Bereich S.Txnfns { if err = writefn (S.CTX, S.TXN, S.PROCESSINGREQ); err! = nil { zurückkehren } } für _, statefulwriteFn: = Bereich s.StatefulTxnfns { if err = statefulwriteFn (S.Ctx, S.TXN, S.Processingreq, S. Processedres); err! = nil { zurückkehren } } zurückkehren }

Sie können weitere Beispiele und Tests im Repo finden -

https://github.com/mahadev-k/go-utils/tree/main/examples?&&&]

Obwohl wir heutzutage zu verteilten Systemen und Konsensprotokoll voreinstimmen, verwenden wir immer noch SQL und es existiert immer noch.

Lassen Sie mich wissen, ob jemand dazu beitragen und darüber aufbauen möchte !!
Danke, dass du so weit gelesen hast !!

https://in.linkedin.com/in/mahadev-k-934520223 https://x.com/mahadev_k_®&&&]

Freigabeerklärung Dieser Artikel wird reproduziert unter: https://dev.to/mahadev_k/building-ransql-transaction-execution-in-go-with-generic-framework-4j0f?1, wenn es zu Verletzungen besteht, bitte [email protected], um es zu löschen.
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3