英文:
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 // <- Declare err
// Use = instead of :=
db, err = sql.Open("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")
...
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 (
"database/sql"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)
var (
prefix = "/api/v1" // 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("mysql", "root:password@unix(/var/run/mysqld/mysqld.sock)/test.com?collation=utf8_general_ci")
if err != nil {
panic("DB connection failed")
}
defer db.Close()
t := tools{db}
r := gin.New()
r.Use(gin.Logger())
r.GET(prefix+"/users", t.dispatch())
r.Run(":3000")
}
答案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("mysql", ...)
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("mysql", ...)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论