2015-07-04 17 views
17

Przeczytałem kilka odpowiedzi StackOverflow na sposób obsługi połączenia db. Ponieważ jest to pula, możemy ją zdefiniować globalnie i używać jej w wielu goroutinach i jest bezpieczna.Udostępnianie zdefiniowanego globalnie db conn z wieloma pakietami w Golang

Problem, który mam, polega na tym, że podzieliłem mój interfejs REST API na wiele pakietów. Każdy z tych pakietów wymaga połączenia db, więc otwieram połączenie z bazą danych podczas uruchamiania. Ale nawet jeśli definiuję połączenie globalnie, jest to tylko na poziomie pakietu. Co mogę zrobić, aby potencjalnie udostępnić go w wielu pakietach?

Dla niektórych kontekstów używam sterownika PostgreSQL i gin-gonic w mojej aplikacji.

+1

Przez "pakietów" masz na myśli różnych projektów/bibliotek? – ZAky

+0

Nie, tylko pakiety wewnętrzne. – user1952811

Odpowiedz

46

Istnieje również możliwość utworzenia innego pakietu do przechowywania ustawień związanych z połączeniem z bazą danych. Może wtedy mieć globalny poziom pakietu, który można zainicjować w main i użyć go w każdym pakiecie, który go importuje.

W ten sposób można wyraźnie zobaczyć, że pakiet bazy danych jest importowany. Oto przykładowy kod.

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

Tak jak w ten sposób. Wydaje się to ładniejszym sposobem przeniesienia wszystkich rzeczy związanych z instalacją bazy danych do tego pakietu. – user1952811

+0

Ładne i czyste. –

+0

Super skuteczny, dzięki! –

10

Prosta odpowiedź: przekazanie zainicjowanej puli połączeń do swoich globalnych pakietów.

np.

// 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. 
} 

Definiujemy globalnych pakiet poziomie, upewnij się one eksportowane (kapitalizowane), a następnie przekazać wskaźnik do naszej puli połączeń do nich.

To jest "w porządku", ale może maskować "gdzie" rzeczy są używane. Jeśli patrzysz na handler'a, może nie być jasne, , z którego pochodzi, zwłaszcza w miarę wzrostu paczki. Bardziej skalowalne podejście może wyglądać jak poniżej:

// 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. 
    } 
} 

Jeśli masz więcej niż tylko połączenia DB jako zależność (np config params, hostów, etc.), owinąć je w things.Env struct lub stuff.Env struct dla każdej paczki.

Przykładem może być funkcja things.New("deps...") *Env, która zwraca skonfigurowany *things.Env, który zawiera zależności używane przez pakiet things.

+2

PS: Napisałem artykuł o tym, jak przekazać struct z zależnościami od twojego handlera lub pakietu: http://elithrar.github.io/article/http-handler-error-handling-revisited/ (aktualizacja starszego artykułu) to może pomóc ci zobaczyć to z innej perspektywy. Zobacz także http://www.jerf.org/iri/post/2929 i https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091, które stanowią dodatkowe podejście do tych podejść. – elithrar

+0

Niesamowite, są dobre. W pewnym sensie irytujące jest to, że muszę przekazać połączenie db dla każdego programu obsługi, ale to czyni go bardzo jednoznacznym. – user1952811

Powiązane problemy