Sharing a globally defined db conn with multiple packages

前端 未结 2 497
感动是毒
感动是毒 2020-12-12 18:30

I\'ve read a few StackOverflow answers on how we handling the db connection. Since it\'s a pool, we can define it globally and use it in multiple goroutines and it\'s safe.

相关标签:
2条回答
  • 2020-12-12 18:54

    Simple answer: pass an initialised connection pool to your packages' own globals.

    e.g.

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

    We define package-level globals, make sure they're exported (capitalised) and then pass a pointer to our connection pool to them.

    This is "okay", but can mask "where" things are being used. If you're looking at a handler it may not be clear where the connection is coming from, especially as your package grows. A more scalable approach might look like the below:

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

    If you have more than just the DB connection as a dependency (e.g. config params, hostnames, etc.) , wrap them in an things.Env struct or a stuff.Env struct for each package.

    An example would be to have a things.New("deps...") *Env function that returns a configured *things.Env that contains the dependencies used by your things package.

    0 讨论(0)
  • 2020-12-12 18:58

    There is also the option of creating another package to hold your database connection-related settings. It can then have a package level global, which can be initialized in main and used in any package that is importing it.

    This way, you can explicitly see that the database package is being imported. Here is some sample code.

    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 讨论(0)
提交回复
热议问题