如何在包中的文件中使用全局var?

lio*_*ssi 30 pointers database-connection go nullreferenceexception

我有以下文件结构:

车型/ db.go

type DB struct {
    *sql.DB
}

var db *DB

func init() {
    dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable",
        DB_USER, DB_PASSWORD, DB_NAME)

    db, err := NewDB(dbinfo)
    checkErr(err)

    rows, err := db.Query("SELECT * FROM profile")
    checkErr(err)

    fmt.Println(rows)
}

func NewDB(dataSourceName string) (*DB, error) {
    db, err := sql.Open("postgres", dataSourceName)
    if err != nil {
        return nil, err
    }
    if err = db.Ping(); err != nil {
        return nil, err
    }
    return &DB{db}, nil
}
Run Code Online (Sandbox Code Playgroud)

车型/ db_util.go

func (p *Profile) InsertProfile() {
    if db != nil {
        _, err := db.Exec(...)
        checkErr(err)
    } else {
        fmt.Println("DB object is NULL")
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试访问dbInsertProfile功能,它说NULL ptr exception.如何访问dbdb_utils.go

我不想大写db(因为它可以访问所有包).

我正在从返回的查询dbinit()正确.

icz*_*cza 40

编辑:问题是您使用了短变量声明, :=并且您只是将创建的*DB值存储在局部变量中而不是全局变量中.

这一行:

db, err := NewDB(dbinfo)
Run Code Online (Sandbox Code Playgroud)

创建2个局部变量:dberr,这个局部变量db与全局db变量无关.您的全局变量将保留nil.您必须将创建的内容分配给*DB全局变量.不要使用简短的变量声明,而是简单的赋值,例如:

var err error
db, err = NewDB(dbinfo)
if err != nil {
    log.Fatal(err)
}
Run Code Online (Sandbox Code Playgroud)

原始答案如下.


它是一种指针类型,您必须在使用它之前对其进行初始化.指针类型的零值是nil.

你不必导出它(这是以大写字母开头的).请注意,只要它们是同一个包的一部分,您就拥有多个文件并不重要,它们可以访问彼此定义的标识符.

一个好的解决方案是在init()自动调用的包函数中执行此操作.

请注意,sql.Open()可能只是在不创建与数据库的连接的情况下验证其参数.要验证数据源名称是否有效,请调用DB.Ping().

例如:

var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("yourdrivername", "somesource")
    if err != nil {
        log.Fatal(err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @lionelmessi我的猜测是,您使用了短变量声明`:=`,而只是将创建的* DB值存储在本地变量中,而不是全局变量中。看不到您的消息来源就无法告诉您,因此请客气并张贴。 (2认同)

End*_*age 12

icza已经正确回答了你的具体问题,但值得为你的错误添加一些额外的解释,以便你了解如何在未来犯错误.在Go中,:=赋值语法创建了新变量,其名称:=可能是阴影包的左侧,甚至是父范围函数/方法变量.举个例子:

package main

import "fmt"

var foo string = "global"

func main() {
    fmt.Println(foo) // prints "global"

    // using := creates a new function scope variable 
    // named foo that shadows the package scope foo
    foo := "function scope" 
    fmt.Println(foo) // prints "function scope"
    printGlobalFoo() // prints "global"

    if true {
        foo := "nested scope"
        fmt.Println(foo) // prints "nested scope"
        printGlobalFoo() // prints "global" 
    } 
    // the foo created inside the if goes out of scope when 
    // the code block is exited

    fmt.Println(foo) // prints "function scope"
    printGlobalFoo() // prints "global"

    if true {
        foo = "nested scope" // note just = not :=
    }

    fmt.Println(foo) // prints "nested scope"
    printGlobalFoo() // prints "global"

    setGlobalFoo()
    printGlobalFoo() // prints "new value"
}

func printGlobalFoo() {
    fmt.Println(foo)
}

func setGlobalFoo() {
    foo = "new value" // note just = not :=
}
Run Code Online (Sandbox Code Playgroud)

注意Go无法删除或取消设置变量,因此一旦您隐藏了更高的范围变量(例如通过创建与包范围变量同名的函数范围变量),就无法访​​问更高的范围该代码块中的变量.

还要注意这:=是一个简写var foo =.两者的行为方式完全相同,但:=只是函数或方法中的有效语法,而var语法在任何地方都有效.


ttr*_*asn 11

对于来到这里并想要快速答复的人。

db.go文件中:

package db

var db *DB

type DB struct {
    *gorm.DB // or what database you want like *mongo.Client
}

func GetDB() *DB {
    if db == nil{
        db = ConnectToYourDbFunc("connection_string")
    }
    return db
}
Run Code Online (Sandbox Code Playgroud)

然后在你的其他包中你可以这样得到它:

db := db.GetDB()
Run Code Online (Sandbox Code Playgroud)

就这样。