2015-07-04 3 views
17

Ich habe ein paar StackOverflow-Antworten gelesen, wie wir die db-Verbindung handhaben. Da es sich um einen Pool handelt, können wir ihn global definieren und in mehreren Gruppen verwenden.Gemeinsame Nutzung einer global definierten db conn mit mehreren Paketen in Golang

Das Problem, das ich habe, ist, dass ich meine REST API in mehrere Pakete aufgeteilt habe. Jedes dieser Pakete benötigt eine db-Verbindung, also öffne ich beim Start eine Datenbankverbindung. Aber selbst wenn ich die Verbindung global definiere, ist es nur auf der Paketebene. Was kann ich tun, um es unter mehreren Paketen zu teilen?

Für einige Kontext verwende ich den PostgreSQL-Treiber und Gin-gonic in meiner Anwendung.

+1

von "Paketen" verschiedene Projekte/Bibliotheken bedeuten? – ZAky

+0

Nein, nur interne Pakete. – user1952811

Antwort

46

Es besteht auch die Möglichkeit, ein anderes Paket zu erstellen, das die Einstellungen für die Datenbankverbindung enthält. Es kann dann eine globale Paketebene haben, die in main initialisiert und in jedem Paket verwendet werden kann, das es importiert.

Auf diese Weise können Sie explizit sehen, dass das Datenbankpaket importiert wird. Hier ist ein Beispielcode.

package database 

var (
    // DBCon is the connection handle 
    // for the database 
    DBCon *sql.DB 
) 

package main 

import "myApp/database" 

func main() { 

    var err error 
    database.DBCon, err = sql.Open("postgres", "user=myname dbname=dbname sslmode=disable") 

} 

package user 

import "myApp/database" 

func Index() { 
    // database handle is available here 
    database.DBCon 

    ... 
} 
+0

Ich mag diesen Weg. Es scheint eine bessere Möglichkeit zu sein, alles, was mit der Datenbankkonfiguration zusammenhängt, in dieses Paket zu verschieben. – user1952811

+0

Schön und sauber. –

+0

Super effektiv, danke! –

10

Einfache Antwort: Übergeben Sie einen initialisierten Verbindungspool an die eigenen Globals Ihrer Pakete.

z.B.

// package stuff 

var DB *sql.DB 

func GetAllStuff() (*Stuff, error) { 
    err := DB.Query("...") 
    // etc. 
} 

// package things 

var DB *sql.DB 

func GetAllThings() (*Thing, error) { 
    err := DB.Query("...") 
    // etc. 
} 

// package main 

func main() { 
    db, err := sql.Open("...") 
    if err != nil { 
     log.Fatal(err) 
    } 

    stuff.DB = db 
    things.DB = db 

    // etc. 
} 

Wir definieren Paketebene Globals, stellen Sie sicher, dass sie exportiert (aktiviert) und dann einen Zeiger auf unsere Verbindungspool an sie weitergeben.

Dies ist "okay", aber kann "wo" Dinge verwendet werden maskieren. Wenn Sie einen Handler betrachten, ist es möglicherweise nicht klar wo die Verbindung herkommt, besonders wenn Ihr Paket wächst. Ein skalierbarer Ansatz könnte wie folgt aussehen:

// package stuff 

type DB struct { 
    *sql.DB 
} 

func New(db *sql.DB) (*DB, error) { 
    // Configure any package-level settings 
    return &DB{db}, nil 
} 

func (db *DB) GetAllStuff() (*Stuff, error) { 
    err := db.Query("...") 
    // etc. 
} 

// package things 

type DB struct { 
    *sql.DB 
} 

func New(db *sql.DB) (*DB, error) { 
    // Configure any package-level settings 
    return &DB{db}, nil 
} 

func (db *DB) GetAllThings() (*Thing, error) { 
    err := db.Query("...") 
    // etc. 
} 

// package main 

func main() { 
    db, err := sql.Open("...") 
    if err != nil { 
     log.Fatal(err) 
    } 

    stuffDB, err := stuff.New(db) 
    if err != nil { 
     log.Fatal(err) 
    } 

    thingsDB, err := things.New(db) 
    if err != nil { 
     log.Fatal(err) 
    } 

    // Simplified. 
    http.HandleFunc("/stuff/all", stuff.ShowStuffHandler(stuffDB)) 
    http.HandleFunc("/things/all", things.ShowThingsHandler(thingsDB)) 

    // etc. 
} 

func ShowStuffHandler(db *stuff.DB) http.HandlerFunc { 
    return func(w http.ResponseWriter, r *http.Request) { 
     // We can use our stuff.DB here 
     stuff, err := db.GetAllStuff() 
     // etc. 
    } 
} 

Wenn Sie mehr als nur die DB-Verbindung als Abhängigkeit (zB Konfigurationsparameter erstellt, Host-Namen, etc.), wickeln Sie sie in einer things.Env Struktur oder eine stuff.Env struct für jedes Paket.

Ein Beispiel wäre eine things.New("deps...") *Env Funktion, die eine konfigurierte *things.Env zurückgibt, die die Abhängigkeiten enthält, die von Ihrem things-Paket verwendet werden.

+2

PS: Ich schrieb einen Artikel darüber, wie man eine Struktur mit Ihrem Handler oder Paket Abhängigkeiten übergeben: http://elithrar.github.io/article/http-handler-error-handling-revisited/ (ein Update für einen älteren Artikel) das könnte dir helfen, es aus einem anderen Blickwinkel zu sehen. Siehe auch http://www.jerf.org/iri/post/2929 und https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091, die zusätzliche Ansätze zu diesen Ansätzen sind. – elithrar

+0

Super, das sind gut. Ich finde es irgendwie ärgerlich, dass ich die db-Verbindung für jeden Handler weitergeben muss, aber das macht es super explizit. – user1952811

Verwandte Themen