Heim Backend-Entwicklung Golang Abfragen, die Sqlc kapseln, um bequemere Transaktionsvorgänge zu implementieren

Abfragen, die Sqlc kapseln, um bequemere Transaktionsvorgänge zu implementieren

Aug 05, 2024 pm 06:39 PM

封装 Sqlc 的 Queries 实现更方便的事务操作

Was ist SQLC?

SQLC ist ein leistungsstarkes Entwicklungstool, dessen Kernfunktion darin besteht, SQL-Abfragen in typsicheren Go-Code umzuwandeln. Durch das Parsen von SQL-Anweisungen und das Analysieren von Datenbankstrukturen kann SQLC automatisch entsprechende Go-Strukturen und -Funktionen generieren, wodurch der Code-Schreibprozess für Datenbankoperationen erheblich vereinfacht wird.

Mit sqlc können sich Entwickler auf das Schreiben von SQL-Abfragen konzentrieren und die mühsame Arbeit der Go-Codegenerierung dem Tool überlassen, wodurch der Entwicklungsprozess beschleunigt und die Codequalität verbessert wird.

SQLC-Transaktionsimplementierung

Der von Sqlc generierte Code enthält normalerweise eine Abfragestruktur, die alle Datenbankoperationen kapselt. Diese Struktur implementiert eine allgemeine Querier-Schnittstelle, die alle Datenbankabfragemethoden definiert.

Der Schlüssel ist, dass die von sqlc generierte neue Funktion jedes Objekt akzeptieren kann, das die DBTX-Schnittstelle implementiert, einschließlich *sql.DB und *sql.Tx.

Der Kern der Transaktionsimplementierung besteht in der Nutzung des Schnittstellenpolymorphismus von Go. Wenn Sie Vorgänge innerhalb einer Transaktion ausführen müssen, erstellen Sie ein *sql.Tx-Objekt und übergeben es dann an die New-Funktion, um eine neue Abfrageinstanz zu erstellen. Diese Instanz führt alle Vorgänge im Kontext einer Transaktion aus.

Angenommen, wir stellen über pgx eine Verbindung zur Postgres-Datenbank her und initialisieren Abfragen mit dem folgenden Code.

var Pool *pgxpool.Pool
var Queries *sqlc.Queries

func init() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()

    connConfig, err := pgxpool.ParseConfig("postgres://user:password@127.0.0.1:5432/db?sslmode=disable")
    if err != nil {
        panic(err)
    }

    pool, err := pgxpool.NewWithConfig(ctx, connConfig)
    if err != nil {
        panic(err)
    }
    if err := pool.Ping(ctx); err != nil {
        panic(err)
    }

    Pool = pool
    Queries = sqlc.New(pool)
}
Nach dem Login kopieren

Kapselung von Transaktionen

Der folgende Code ist eine clevere SQLC-Transaktionskapselung, die den Prozess der Verwendung von Datenbanktransaktionen in Go vereinfacht. Die Funktion akzeptiert einen Kontext und eine Rückruffunktion als Parameter. Diese Rückruffunktion ist die spezifische Operation, die der Benutzer in der Transaktion ausführen möchte.

func WithTransaction(ctx context.Context, callback func(qtx *sqlc.Queries) (err error)) (err error) {
    tx, err := Pool.Begin(ctx)
    if err != nil {
        return err
    }
    defer func() {
        if e := tx.Rollback(ctx); e != nil && !errors.Is(e, pgx.ErrTxClosed) {
            err = e
        }
    }()

    if err := callback(Queries.WithTx(tx)); err != nil {
        return err
    }

    return tx.Commit(ctx)
}
Nach dem Login kopieren

Die Funktion startet zunächst eine neue Transaktion und verzögert dann die Ausführung, um sicherzustellen, dass die Transaktion schließlich zurückgesetzt wird, sofern sie nicht explizit festgeschrieben wird. Dies ist ein Sicherheitsmechanismus, um zu verhindern, dass nicht abgeschlossene Transaktionen Ressourcen belegen. Als Nächstes ruft die Funktion den vom Benutzer bereitgestellten Rückruf auf und übergibt ein Abfrageobjekt mit einem Transaktionskontext, sodass der Benutzer die erforderlichen Datenbankoperationen innerhalb der Transaktion ausführen kann.

Wenn der Rückruf erfolgreich und ohne Fehler ausgeführt wird, schreibt die Funktion die Transaktion fest. Alle während des Vorgangs auftretenden Fehler führen dazu, dass die Transaktion zurückgesetzt wird. Diese Methode stellt nicht nur die Datenkonsistenz sicher, sondern vereinfacht auch die Fehlerbehandlung erheblich.

Die Eleganz dieser Kapselung besteht darin, dass sie komplexe Transaktionsverwaltungslogik hinter einem einfachen Funktionsaufruf verbirgt. Benutzer können sich auf das Schreiben von Geschäftslogik konzentrieren, ohne sich Gedanken über das Starten, Festschreiben oder Zurücksetzen von Transaktionen machen zu müssen.

Die Verwendung dieses Codes ist recht intuitiv. Sie können die Funktion db.WithTransaction dort aufrufen, wo Sie eine Transaktion ausführen müssen, indem Sie eine Funktion als Parameter übergeben, der alle Datenbankoperationen definiert, die Sie innerhalb der Transaktion ausführen möchten.

err := db.WithTransaction(ctx, func(qtx *sqlc.Queries) error {
    // 在这里执行你的数据库操作
    // 例如:
    _, err := qtx.CreateUser(ctx, sqlc.CreateUserParams{
        Name: "Alice",
        Email: "alice@example.com",
    })
    if err != nil {
        return err
    }

    _, err = qtx.CreatePost(ctx, sqlc.CreatePostParams{
        Title: "First Post",
        Content: "Hello, World!",
        AuthorID: newUserID,
    })
    if err != nil {
        return err
    }

    // 如果所有操作都成功,返回 nil
    return nil
})

if err != nil {
    // 处理错误
    log.Printf("transaction failed: %v", err)
} else {
    log.Println("transaction completed successfully")
}
Nach dem Login kopieren

In diesem Beispiel erstellen wir einen Benutzer und einen Beitrag in einer Transaktion. Wenn ein Vorgang fehlschlägt, wird die gesamte Transaktion zurückgesetzt. Wenn alle Vorgänge erfolgreich sind, wird die Transaktion festgeschrieben.

Der Vorteil dieses Ansatzes besteht darin, dass Sie den Start, das Commit oder das Rollback der Transaktion nicht manuell verwalten müssen. All dies wird von der Funktion db.WithTransaction erledigt. Sie müssen sich nur auf die tatsächlichen Datenbankoperationen konzentrieren, die innerhalb der Transaktion ausgeführt werden. Dadurch wird der Code erheblich vereinfacht und die Möglichkeit von Fehlern verringert.

Weitere Verpackung

Die oben erwähnte Verpackungsmethode ist nicht ohne Mängel.

Diese einfache Transaktionskapselung weist Einschränkungen beim Umgang mit verschachtelten Transaktionen auf. Dies liegt daran, dass jedes Mal eine neue Transaktion erstellt wird, anstatt zu prüfen, ob Sie sich bereits in einer befinden.

Um die verschachtelte Transaktionsverarbeitung zu implementieren, müssen wir das aktuelle Transaktionsobjekt abrufen, aber das aktuelle Transaktionsobjekt ist in sqlc.Queries verborgen, daher müssen wir sqlc.Queries erweitern.

Die Struktur, die sqlc.Queries erweitert, wird von uns als Repositories erstellt. Sie erweitert *sqlc.Queries und fügt einen neuen Attributpool hinzu, der ein Zeiger vom Typ pgxpool.Pool ist.

type Repositories struct {
    *sqlc.Queries
    pool *pgxpool.Pool
}

func NewRepositories(pool *pgxpool.Pool) *Repositories {
    return &Repositories{
        pool:    pool,
        Queries: sqlc.New(pool),
    }
}
Nach dem Login kopieren

Aber wenn wir mit dem Schreiben von Code beginnen, werden wir feststellen, dass *pgxpool.Pool die pgx.Tx-Schnittstelle nicht erfüllt. Dies liegt daran, dass *pgxpool.Pool die Methoden Rollback und Commit fehlen. Es enthält nur Begin zum Starten einer Transaktion. Methode: Um dieses Problem zu lösen, erweitern wir Repositories weiter, fügen ihr ein neues Attribut tx und eine neue NewRepositoriesTx-Methode hinzu.

type Repositories struct {
    *sqlc.Queries
    tx   pgx.Tx
    pool *pgxpool.Pool
}

func NewRepositoriesTx(tx pgx.Tx) *Repositories {
    return &Repositories{
        tx:      tx,
        Queries: sqlc.New(tx),
    }
}
Nach dem Login kopieren

Nun gibt es in unserer Repository-Struktur sowohl Pool- als auch TX-Attribute. Das sieht vielleicht nicht sehr elegant aus. Warum können wir nicht einen einheitlichen TX-Typ abstrahieren, nämlich *pgxpool.Pool? Es gibt nur eine Methode zum Starten einer Transaktion, aber keine Methode zum Beenden der Transaktion. Eine Möglichkeit, dieses Problem zu lösen, besteht darin, eine andere RepositoriesTX-Struktur zu erstellen und darin pgx.Tx anstelle von *pgxpool.Pool zu speichern. Dies kann jedoch zu There führen Eine davon ist, dass wir möglicherweise die WithTransaction-Methode für beide implementieren müssen. Über die andere Frage werden wir nun zuerst die WithTransaction-Methode von Repositories implementieren.

func (r *Repositories) WithTransaction(ctx context.Context, fn func(qtx *Repositories) (err error)) (err error) {
    var tx pgx.Tx
    if r.tx != nil {
        tx, err = r.tx.Begin(ctx)
    } else {
        tx, err = r.pool.Begin(ctx)
    }
    if err != nil {
        return err
    }
    defer func() {
        if e := tx.Rollback(ctx); e != nil && !errors.Is(e, pgx.ErrTxClosed) {
            err = e
        }
    }()

    if err := fn(NewRepositoriesTx(tx)); err != nil {
        return err
    }

    return tx.Commit(ctx)
}
Nach dem Login kopieren

这个方法和上一章节实现的 WithTransaction 主要不同是,他是实现在 *Repositories 上面而不是全局的,这样我们就可以通过 (r *Repositories) 中的 pgx.Tx 来开始嵌套事务了。

在没有开始事务的时候,我们可以调用 repositories.WithTransaction 来开启一个新的事务。

err := db.repositories.WithTransaction(ctx, func(tx *db.Repositories) error {

    return nil
})
Nach dem Login kopieren

多级事务也是没有问题的,非常容易实现。

err := db.repositories.WithTransaction(ctx, func(tx *db.Repositories) error {
    // 假设此处进行了一些数据操作
    // 然后,开启一个嵌套事务
    return tx.WithTransaction(ctx, func(tx *db.Repositories) error {
        // 这里可以在嵌套事务中进行一些操作
        return nil
    })
})
Nach dem Login kopieren

这个封装方案有效地确保了操作的原子性,即使其中任何一个操作失败,整个事务也会被回滚,从而保障了数据的一致性。

结束语

本文介绍了一个使用 Go 和 pgx 库封装 SQLC 数据库事务的方案。

核心是 Repositories 结构体,它封装了 SQLC 查询接口和事务处理逻辑。通过 WithTransaction 方法,我们可以在现有事务上开始新的子事务或在连接池中开始新的事务,并确保在函数返回时回滚事务。

构造函数 NewRepositories 和 NewRepositoriesTx 分别用于创建普通和事务内的 Repositories 实例。

这样可以将多个数据库操作封装在一个事务中,如果任何一个操作失败,事务将被回滚,提高了代码的可维护性和可读性。

Das obige ist der detaillierte Inhalt vonAbfragen, die Sqlc kapseln, um bequemere Transaktionsvorgänge zu implementieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

<🎜>: Bubble Gum Simulator Infinity - So erhalten und verwenden Sie Royal Keys
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusionssystem, erklärt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Flüstern des Hexenbaum
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Java-Tutorial
1671
14
PHP-Tutorial
1276
29
C#-Tutorial
1256
24
Golang gegen Python: Leistung und Skalierbarkeit Golang gegen Python: Leistung und Skalierbarkeit Apr 19, 2025 am 12:18 AM

Golang ist in Bezug auf Leistung und Skalierbarkeit besser als Python. 1) Golangs Kompilierungseigenschaften und effizientes Parallelitätsmodell machen es in hohen Parallelitätsszenarien gut ab. 2) Python wird als interpretierte Sprache langsam ausgeführt, kann aber die Leistung durch Tools wie Cython optimieren.

Golang und C: Parallelität gegen Rohgeschwindigkeit Golang und C: Parallelität gegen Rohgeschwindigkeit Apr 21, 2025 am 12:16 AM

Golang ist in Gleichzeitigkeit besser als C, während C bei Rohgeschwindigkeit besser als Golang ist. 1) Golang erreicht durch Goroutine und Kanal eine effiziente Parallelität, die zum Umgang mit einer großen Anzahl von gleichzeitigen Aufgaben geeignet ist. 2) C über Compiler -Optimierung und Standardbibliothek bietet es eine hohe Leistung in der Nähe der Hardware, die für Anwendungen geeignet ist, die eine extreme Optimierung erfordern.

Erste Schritte mit Go: Ein Anfängerführer Erste Schritte mit Go: Ein Anfängerführer Apr 26, 2025 am 12:21 AM

GoisidealforBeginersandSuitableforCloudandNetWorkServicesDuetoitsSimplicity, Effizienz und Konsumfeaturen.1) InstallgoFromTheofficialwebSiteAnDverifyWith'goversion'.2) CreateAneDrunyourFirstProgramwith'gorunhello.go.go.go.

Golang gegen C: Leistung und Geschwindigkeitsvergleich Golang gegen C: Leistung und Geschwindigkeitsvergleich Apr 21, 2025 am 12:13 AM

Golang ist für schnelle Entwicklung und gleichzeitige Szenarien geeignet, und C ist für Szenarien geeignet, in denen extreme Leistung und Kontrolle auf niedriger Ebene erforderlich sind. 1) Golang verbessert die Leistung durch Müllsammlung und Parallelitätsmechanismen und eignet sich für die Entwicklung von Webdiensten mit hoher Konsequenz. 2) C erreicht die endgültige Leistung durch das manuelle Speicherverwaltung und die Compiler -Optimierung und eignet sich für eingebettete Systementwicklung.

Golangs Auswirkungen: Geschwindigkeit, Effizienz und Einfachheit Golangs Auswirkungen: Geschwindigkeit, Effizienz und Einfachheit Apr 14, 2025 am 12:11 AM

GoimpactsDevelopmentPositivyThroughSpeed, Effizienz und DiasMlitication.1) Geschwindigkeit: Gocompilesquickandrunseffiction, idealforlargeProjects

Golang gegen Python: Schlüsselunterschiede und Ähnlichkeiten Golang gegen Python: Schlüsselunterschiede und Ähnlichkeiten Apr 17, 2025 am 12:15 AM

Golang und Python haben jeweils ihre eigenen Vorteile: Golang ist für hohe Leistung und gleichzeitige Programmierung geeignet, während Python für Datenwissenschaft und Webentwicklung geeignet ist. Golang ist bekannt für sein Parallelitätsmodell und seine effiziente Leistung, während Python für sein Ökosystem für die kurze Syntax und sein reiches Bibliothek bekannt ist.

Golang und C: Die Kompromisse bei der Leistung Golang und C: Die Kompromisse bei der Leistung Apr 17, 2025 am 12:18 AM

Die Leistungsunterschiede zwischen Golang und C spiegeln sich hauptsächlich in der Speicherverwaltung, der Kompilierungsoptimierung und der Laufzeiteffizienz wider. 1) Golangs Müllsammlung Mechanismus ist praktisch, kann jedoch die Leistung beeinflussen.

Das Performance -Rennen: Golang gegen C. Das Performance -Rennen: Golang gegen C. Apr 16, 2025 am 12:07 AM

Golang und C haben jeweils ihre eigenen Vorteile bei Leistungswettbewerben: 1) Golang ist für eine hohe Parallelität und schnelle Entwicklung geeignet, und 2) C bietet eine höhere Leistung und eine feinkörnige Kontrolle. Die Auswahl sollte auf Projektanforderungen und Teamtechnologie -Stack basieren.

See all articles