GoLang: 在数据库中失去作用域

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

GoLang: Losing scope in database

问题

你好!根据你的描述,你想要关于如何在Go应用程序中管理和重用数据库连接的指导。你已经将数据库代码拆分为一个包,以便在处理SQL查询的包中进行数据库查找,就像一个仓储层。你希望能够灵活地将数据库层与应用程序服务解耦,以便在必要时可以轻松替换数据库。

基本上,你想要一些关于何时以及如何处理SQL连接以及在应用程序中持有一个sql.DB指针的指导。你是否需要在main.go中持有一个全局指针,还是可以在mysql包中管理连接?

根据你提供的代码,你在mysqlstorage包中定义了一个名为db的全局变量,并在Connect函数中初始化了它。你可以在SaveUser和GetUser函数中使用这个db变量来执行数据库操作。

然而,根据你提供的错误信息,似乎在运行main.go并尝试保存用户时,你遇到了一个问题,似乎丢失了指针的作用域。这可能是因为你在main.go中没有调用Connect函数来初始化db变量,导致db变量仍然为nil。

为了解决这个问题,你可以在main.go中调用Connect函数来初始化db变量,确保在使用db变量之前它已经被正确初始化。

希望这些信息对你有帮助!如果你还有其他问题,请随时提问。

英文:

New to Go, come from .Net-land so bear with me... Would love some guidance on architecting my Go app specifically with regard to managing and reusing DB connections.

I have split up my db code into a package to allow for db-lookups into package that handles sql lookups like a repository layer. I want to have flexibility to decouple my db layer from my app services, so I can easily replace my database if necessary.

Basically I am looking for some guidance on how and when to handle sql connect and holding a sql.DB pointer in the app. Do I need to hold onto a global pointer in main.go or can I manage the connection in my mysql package?

Here's my code:

package mysqlstorage

import (
	"database/sql"
	"fmt"
	"log"
	"types"
)

var db *sql.DB

func Connect() {
	db, dberr := sql.Open(“<CONNECTION_STRING>“)

	if dberr != nil {
		fmt.Println(dberr)
	}
}

func SaveUser(u types.User) {
    // use db here! 
    ....
}

func GetUser(id string) types.User {
    // use db here!
    ....
}

On running my main.go and using my userservices package to attempt to save a user, I hit a problem where I appear to lose scope of my pointer:-

2015/05/03 17:49:08 http: panic serving [::1]:50106: runtime error: invalid memory address or nil pointer dereference goroutine 7 [running]: net/http.func·011() 
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1130 +0xbb database/sql.(*DB).conn(0x0, 0x10, 0x0, 0x0) 	
/usr/local/Cellar/go/1.4.2/libexec/src/database/sql/sql.go:634 +0x7ae database/sql.(*DB).Ping(0x0, 0x0, 0x0) 	
/usr/local/Cellar/go/1.4.2/libexec/src/database/sql/sql.go:462 +0x3a mysqlstorage.SaveUser(0x0, 0xc20805425a, 0x7, 0xc208054280, 0x11, 0xc208054268, 0x6, 0xc208054274, 0x5) 	
/Users/<USERNAME>/Desktop/go/<APPNAME>/api/src/mysqlstorage/mysqlstorage.go:24
    +0x35 services.CreateUser(0x57c148, 0xc2080563c0, 0xc2080329c0) 	
/Users/<USERNAME>/Desktop/go/<APPNAME>/api/src/services/userservices.go:30
    +0x398 net/http.HandlerFunc.ServeHTTP(0x3d02a0, 0x57c148, 0xc2080563c0, 0xc2080329c0) 	
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1265 +0x41 github.com/gorilla/mux.(*Router).ServeHTTP(0xc20803c140, 0x57c148, 0xc2080563c0, 0xc2080329c0) 	
/Users/<USERNAME>/Desktop/go/<APPNAME>/api/src/github.com/gorilla/mux/mux.go:98
    +0x297 net/http.(*ServeMux).ServeHTTP(0xc20803a720, 0x57c148, 0xc2080563c0, 0xc2080329c0) 	
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1541 +0x17d net/http.serverHandler.ServeHTTP(0xc2080543c0, 0x57c148, 0xc2080563c0, 0xc2080329c0) 	
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1703 +0x19a net/http.(*conn).serve(0xc208056320) 	
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1204 +0xb57 created by net/http.(*Server).Serve 	
/usr/local/Cellar/go/1.4.2/libexec/src/net/http/server.go:1751 +0x35e

Any guidance would appreciated! Thanks guys!

答案1

得分: 4

我认为你的问题是在Connect()作用域中,db:=操作符导致被遮蔽了。如果你改变方法来声明一个dberr变量:

func Connect() {
    var dberr error
    db, dberr = sql.Open("<CONNECTION_STRING>")

    if dberr != nil {
        fmt.Println(dberr)
    }
}

你的代码将按预期工作。

英文:

I think that your problem is that db in the Connect() scope is shadowed due to your use of the := operator. If you change your method to declare a dberr var:

func Connect() {
    var dberr error
    db, dberr = sql.Open(“&lt;CONNECTION_STRING&gt;“)

    if dberr != nil {
        fmt.Println(dberr)
    }
}

Your code will work as intended

答案2

得分: 3

根据@Lander的说明,你的超出范围问题是因为你通过db, err :=赋值创建了一个局部变量db,当Connect函数返回时,该变量就超出了作用域。

根据database/sql包的说明:

DB是表示零个或多个底层连接池的数据库句柄。它可以被多个goroutine并发使用。

sql包会自动创建和释放连接,同时还会维护一个空闲连接的池。

并且

返回的DB可以被多个goroutine并发使用,并且它维护着自己的空闲连接池。因此,应该只调用一次Open函数。很少需要关闭DB。

也就是说,你希望在整个进程的生命周期中只打开一次连接并保持打开状态。为了在完成时(或遇到致命错误或恐慌时)可靠地关闭连接,你可以在打开连接后立即使用典型的Go语言defer db.Close(),这应该在主函数中完成。

你仍然可以将Connect函数保留在你的包中,并使用它来初始化全局包变量db,但是在主函数中调用它一次,并将*sql.DB返回给主函数,这样你就可以使用defer db.Close()来关闭连接。

英文:

As noted by @Lander your out-of-scope happens because you are creating a local variable db through the db, err := assignment which then goes out of scope when the Connect func returns.

Generally on the use of sql.DB

From the database/sql package:

> DB is a database handle representing a pool of zero or more underlying connections. It's safe for concurrent use by multiple goroutines.
>
>The sql package creates and frees connections automatically; it also maintains a free pool of idle connections.

and

> The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.

That said you want to open the connection once and then keep it open for the duration of the process. With that the only reliable way to actually close it when you are done (or run into a fatal or panic) would be to do the idiomatic go defer db.Close() right after opening it which would need to be done in the main function.

You could still keep your Connect function in your package and also use it to initialize the global package variable db, but call it from main once and return *sql.DB to main so you can defer db.Close().

huangapple
  • 本文由 发表于 2015年5月4日 23:52:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/30034384.html
匿名

发表评论

匿名网友

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

确定