在b.go中无法使用在a.go中声明的db *sql.DB。

huangapple go评论92阅读模式
英文:

db *sql.DB declared in a.go not available in b.go

问题

我有两个.go文件:a.go和b.go。

我正在为我的MySQL数据库连接声明一个全局的db *sql.DB。

我的目标是在所有的包文件中只声明一次db,并在这种情况下在b.go中使用它。

一切都构建得很好,但是当访问我的API端点/users时,我会收到一个错误。

a.go文件内容如下:

package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
)

var (
	prefix string = "/api/v1" // API前缀
	db     *sql.DB
)

// 启动整个应用
func main() {
	// 设置数据库
	db, err := sql.Open("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")
	if err != nil {
		panic(err)
	}

	defer db.Close()

	err = db.Ping()
	if err != nil {
		panic(err)
	}

	r := gin.New()

	r.Use(gin.Logger())

	r.GET(prefix+"/users", func(c *gin.Context) {

		t := GetUsers()

		c.JSON(200, t)
	})

	r.Run(":3000")
}

b.go文件内容如下:

package main

import (
	"log"
)

type User struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

func GetUsers() []User {
	a := []User{}

	_, err := db.Query("SELECT name FROM users")
	if err != nil {
		log.Fatal(err)
	}

	return a
}

希望这可以帮助到你!

英文:

I have two .go files: a.go and b.go

I'm declaring a global db *sql.DB for my mysql database connection.

My goal is to declare db once and use it in all my package files, in this case b.go.

Everything builds fine, but I get an error when hitting my API endpoint /users

22:48:52 app         | 2015/05/18 22:48:52 http: panic serving 127.0.0.1:55742: runtime error: invalid memory address or nil pointer dereference
goroutine 6 [running]:
net/http.func·011()
	/usr/local/go/src/net/http/server.go:1130 +0xbb
database/sql.(*DB).conn(0x0, 0x4da104, 0x0, 0x0)
	/usr/local/go/src/database/sql/sql.go:634 +0x7ae
database/sql.(*DB).query(0x0, 0x809e70, 0x16, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0)
	/usr/local/go/src/database/sql/sql.go:933 +0x43
database/sql.(*DB).Query(0x0, 0x809e70, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
	/usr/local/go/src/database/sql/sql.go:924 +0xa6
main.GetUsers(0x0, 0x0, 0x0)
	/var/www/zazok.com/api/src/app/user.go:15 +0xc0
main.func·001(0xc2080424d0)
	/var/www/zazok.com/api/src/app/app.go:35 +0x1f
github.com/gin-gonic/gin.(*Context).Next(0xc2080424d0)
	/var/www/zazok.com/api/src/github.com/gin-gonic/gin/context.go:114 +0x95
github.com/gin-gonic/gin.func·006(0xc2080424d0)
	/var/www/zazok.com/api/src/github.com/gin-gonic/gin/logger.go:49 +0x68
github.com/gin-gonic/gin.(*Context).Next(0xc2080424d0)
	/var/www/zazok.com/api/src/github.com/gin-gonic/gin/context.go:114 +0x95
github.com/gin-gonic/gin.func·009(0x7f1123351408, 0xc208036280, 0xc2080328f0, 0x0, 0x0, 0x0)
	/var/www/zazok.com/api/src/github.com/gin-gonic/gin/routergroup.go:57 +0xa3
github.com/julienschmidt/httprouter.(*Router).ServeHTTP(0xc20803af60, 0x7f1123351408, 0xc208036280, 0xc2080328f0)
	/var/www/zazok.com/api/src/github.com/julienschmidt/httprouter/router.go:299 +0x18e
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc208042000, 0x7f1123351408, 0xc208036280, 0xc2080328f0)
	/var/www/zazok.com/api/src/github.com/gin-gonic/gin/gin.go:156 +0x4d
net/http.serverHandler.ServeHTTP(0xc208030120, 0x7f1123351408, 0xc208036280, 0xc2080328f0)
	/usr/local/go/src/net/http/server.go:1703 +0x19a
net/http.(*conn).serve(0xc2080361e0)
	/usr/local/go/src/net/http/server.go:1204 +0xb57
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:1751 +0x35e

a.go

package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
)

var (
	prefix string = "/api/v1" // API prefix
	db     *sql.DB
)

// Boots up this whole thing
func main() {
	// Setting up DB
	db, err := sql.Open("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")
	if err != nil {
		panic(err)
	}

	defer db.Close()

	err = db.Ping()
	if err != nil {
		panic(err)
	}

    r := gin.New()

	r.Use(gin.Logger())

	r.GET(prefix+"/users", func(c *gin.Context) {

        t := GetUsers()

        c.JSON(200, t)
	})

	r.Run(":3000")
}

b.go

package main

import (
	"log"
)

type User struct {
	Id          int    `json:"id"`
	Name        string `json:"name"`
}

func GetUsers() []User {
	a := []User{}

	_, err := db.Query("SELECT name FROM users")
	if err != nil {
		log.Fatal(err)
	}

	return a
}

答案1

得分: 3

编辑
正如DaveC指出的那样,问题在于使用:=只在局部范围内初始化变量。在之前声明err将导致sql.Open将连接保存在全局而不是创建一个新的局部变量,如下所示:

func main() {
    var err error // <- 声明err

    // 使用=而不是:=
    db, err = sql.Open("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")
    ...
}

此编辑受到了DaveC、tomasz和Ben Darnell的影响(Ben Darnell的答案:https://stackoverflow.com/questions/28284138/go-global-variable-and-short-variable-definition)

/编辑

个人而言,我更喜欢尽量避免使用全局变量。为了做到这一点,将GetUsers更改为接受一个*sql.DB参数。然后,将a.go更改为具有自定义的gin.HandlerFunc,它是一个数据库接收器。在我创建的示例中,我创建了一个tools结构体,您可以使用它来传递许多“全局”事物。

package main

import (
	"database/sql"

	"github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
)

var (
	prefix = "/api/v1" // API前缀
)

type tools struct {
	db *sql.DB
}

func (t tools) dispatch() gin.HandlerFunc {
	return func(c *gin.Context) {
		GetUsers(t.db)
	}
}

// 启动整个应用
func main() {
	// 设置数据库连接
	db, err := sql.Open("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")

	if err != nil {
		panic("数据库连接失败")
	}
	defer db.Close()

	t := tools{db}

	r := gin.New()

	r.Use(gin.Logger())

	r.GET(prefix+"/users", t.dispatch())

	r.Run(":3000")
}
英文:

EDIT:
As pointed out by DaveC, the problem is that using := initiates a variable in the local scope only. Declaring err beforehand will cause the sql.Open to save the connection in the global instead of creating a new local, as follows:

func main() {
    var err error // &lt;- Declare err

	// Use = instead of :=
	db, err = sql.Open(&quot;mysql&quot;, &quot;root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci&quot;)
    ...

This edit influenced by DaveC, tomasz, and Ben Darnell (Ben Darnell's answer: https://stackoverflow.com/questions/28284138/go-global-variable-and-short-variable-definition)

/EDIT

Personally, I prefer to avoid globals where possible. To do this, change GetUsers to take an argument *sql.DB. Then change your a.go to have a custom gin.HandlerFunc that is a database receiver. In the example I've created a tools struct that you could use to pass in many "global" things.

package main

import (
	&quot;database/sql&quot;

	&quot;github.com/gin-gonic/gin&quot;
    _ &quot;github.com/go-sql-driver/mysql&quot;
)

var (
	prefix = &quot;/api/v1&quot; // API prefix
)

type tools struct {
	db *sql.DB
}

func (t tools) dispatch() gin.HandlerFunc {
	return func(c *gin.Context) {
		GetUsers(t.db)
	}
}

// Boots up this whole thing
func main() {
	// Setting up DB
	db, err := sql.Open(&quot;mysql&quot;, &quot;root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci&quot;)

	if err != nil {
		panic(&quot;DB connection failed&quot;)
	}
	defer db.Close()

	t := tools{db}

	r := gin.New()

	r.Use(gin.Logger())

	r.GET(prefix+&quot;/users&quot;, t.dispatch())

	r.Run(&quot;:3000&quot;)
}

答案2

得分: 3

我认为这是一个经典的Go bug,与使用多个文件无关。

这个语句:

db, err := sql.Open("mysql", ...)

在主作用域中声明了一个新的变量db(注意:=),而你的全局变量保持不变(为nil)。

你可以很容易地修复它:

var err error
db, err = sql.Open("mysql", ...)
英文:

I think this is a classic Go bug and it has nothing to do with using multiple files.

This statement:

db, err := sql.Open(&quot;mysql&quot;, ...)

declares new variable db in a main scope (note :=) and your global variable is untouched (nil).

You can easily fix it:

var err error
db, err = sql.Open(&quot;mysql&quot;, ...)

huangapple
  • 本文由 发表于 2015年5月19日 10:54:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/30315951.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定