Go-变量已初始化且非nil,但对于其他函数则为nil [英] Go - Variable initialized and non nil, but nil for other functions

查看:43
本文介绍了Go-变量已初始化且非nil,但对于其他函数则为nil的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要写一个与我当前问题相匹配的标题有点困难.我有一个main()函数,该函数在另一个包(database_sql)中使用一个函数.此函数初始化全局变量sql.DB *.初始化之后,该变量不是nil,但是对于其他函数,该变量仍然是nil.让我们看下面的代码!

It's a bit hard to write a title that match with my current problem.. I have a main() function which uses a function in another package (database_sql). This function initializes a global variable sql.DB*. After the initialization, the variable isn't nil, but for the others functions, this variable is still nil.. Let see the code below !

main.go

package main

import (
    "net/http"
    db "./database_sql"
    router "./router"
)

func main() {

    db.Init_SQL();

    router.Init_routes()

    http.ListenAndServe(":8080", router.GetRouter())

}

db.go

package database_sql

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

var DB *sql.DB // global variable to share it between main and the HTTP handler                                                                                                                              
//var dbi *databaseinfos                                                                                                                                                                                     

func Init_SQL() {
    dbi := databaseinfos{
            user: "something",
            password: "something",
            name: "something",
            address: "something",
            port: "2323",
            url: ""}
    dbi.url = (dbi.user + ":" + dbi.password + "@tcp(" + dbi.address + ":" + dbi.port + ")/" + dbi.name)

    db_init_var, err := sql.Open("mysql", dbi.url)
    if err != nil {
            fmt.Println("Error on initializing database connection: %s", err.Error())
    }

    err = db_init_var.Ping()
    if err != nil {
            fmt.Println("Error on opening database connection: %s", err.Error())
    }

    // Here, as you can test, my variable is initialized
    if err == nil {
            DB = db_init_var
            if DB == nil {
                    fmt.Println("NIL DB !!")
            }
            if db_init_var == nil {
                    fmt.Println("NIL db_init_var !!")
            }
            fmt.Println(dbi.url)
    }
}

现在,一切都很好!现在,我将使用用户名/密码测试 http://xxxxxxx/login .一切正常,现在,是时候使用上面声明的DB变量在数据库中进行请求了.

Now, everything is ok ! I will now test http://xxxxxxx/login with an username/password. Everything's working fine and now, it's the time to make a request in the database with my DB variable declared above.

request_user.go

request_user.go

package database_sql

import (
    "encoding/json"
    "fmt"
    "net/http"
    m "models"
)

func CanLogin(user m.User) (bool, m.User, m.StatusBack) {

    // Prepare statement for reading data                                                                                                                                                                
    stmtOut, err := DB.Prepare("SELECT username, password, token FROM users WHERE username = ? AND password = ?")
    if err != nil {
            return false, user, m.StatusBack{ToString: err.Error(), IdStatus: http.StatusInternalServerError}
    }
defer stmtOut.Close()

    // Query the user                                                                                                                                                                                    
err = stmtOut.QueryRow(user.Username, user.Password).Scan(&user.Username, &user.Password, &user.UUID) // WHERE user is registered                                                                    
    if err != nil {
            return false, user, m.StatusBack{ToString: "User not found.", IdStatus: http.StatusNotFound}
    } else {

            j, err := json.Marshal(user)
            if err == nil {
                    return true, user, m.StatusBack{ToString: string(j), IdStatus: http.StatusOK}
            } else {
                    return false, user, m.StatusBack{ToString: err.Error(), IdStatus: http.StatusInternalServerError}
    }
    }

}

但是,当我发出sql请求时,下一行说DB为零. stmtOut,错误:= DB.Prepare("SELECT用户名,密码,来自用户的令牌,其中用户名=?AND密码=?")

However, when I'm making my sql request, the following line says that DB is nil. stmtOut, err := DB.Prepare("SELECT username, password, token FROM users WHERE username = ? AND password = ?")

我做了很多测试,例如尝试使用'='而不是':='对其进行初始化.我尝试了'if DB == nil'语句,而DB始终为n ..为什么?我先初始化数据库变量,然后再初始化路由器,因此从技术上讲,当路由器将重定向功能重定向到该函数时,该函数将不会使用数据库变量(DB)nil,不是吗?

I made lot of test such as trying to initialize it with '=' and no ':='. I tried 'if DB == nil' statement and DB is always nil.. Why? I initialize my database var before initialize my router, so technically, when my router give a redirection to a function, this function wouldn't use a database var (DB) nil, isn't?

感谢您的回答!

编辑2 我回答了您的答案,然后像您要求的那样对代码进行了编辑,并添加了一些内容.在init_SQL之后,数据库不是nil!

EDIT 2 I red your answers and then, I edited my code like you asked with some adds. After init_SQL, DB isn't nil !

main.go

package main

import (
    "fmt"
    "net/http"
    db "./database_sql"
    router "./router"
)

func main() {

    if db.Init_SQL() == true {

            if db.DB == nil {
                    fmt.Println("db.DB is nil !")
            }

            if router.Init_routes() == true {

                    http.ListenAndServe(":8080", router.GetRouter())

            }
    }
}

因此,现在我的2个init函数在成功时返回true,在失败时返回false.我认为这可能是一个异步问题,我这样做是为了等待每个功能的结束以继续我的逐步"操作

So, now my 2 init functions return true on success and false on failure. I thought it could be an asynchronous problem, I made it like that to wait the end of each functions to continue my 'step by step'

推荐答案

首先,导出这样的全局变量并不是idomatic.复杂类型应由运行时初始化(您正在使用 main()进行初始化),但会从运行时全局保留,以控制处置.

First, it is not idomatic to export a global var like that. Complex types should be initialized by the runtime (which you are doing with main()), but retained globally from the runtime to control the disposal.

您还缺少 db.Close().

我相信几年前第一次使用MySQL时,我遇到了与您的模式相同的问题.创建本地范围的指针,然后将其分配给全局变量的方式有些奇怪.通常最好直接将其分配给全局变量.但是我认为核心问题是您需要将指针分配给指针.

I believe I ran I to the same issue with your pattern a few years ago when first using MySQL. There is an oddness with the way one creates a local scoped pointer, and then assign it to a global var. It is usually better to assign it directly to global var. But I think the core issue is you need to assign the pointer to the pointer.

我使用的模式是将可测试的数据库/sql * DB 保持在初始化它的全局状态.为什么要在轮子工作时重新发明轮子:

The pattern I use is to keep the testable database/sql *DB in the global state where I initialize it. Why reinvent the wheel when the wheel works:

package main

import ( 
  ...
  "database/sql"

  "mypackage-that-inits-db/database"
)

var db *sql.DB

find main() {

  var err error
  db, err = database.Init_SQL(...params)
  if err != nil {
    ...
  }
  defer db.Close()

  InitUsers(db)
  InitStats(db)
  ...

  http.ListenAndServe(...)

  // now inject db global dependency in other code
  //

  globalStats := NewStatsEngine(db)
  globalStats.RecordUpdateToSomething(...)

  users := getUsers(db)

   ... etc

}

通常是您在其他代码中看到的模式.

That's typically the pattern you see in other code.

请注意对延迟和关闭的控制,它在调用方中位于此处.

Note the control over defer and Close, where it belongs here in caller.

还要注意,您现在可以通过创建模拟 sql.DB 对象并将其注入NewStatsEngine,getUsers等对象中,从而轻松测试其他程序包中的代码,而不必模拟全局变量,测试,拆卸并再次设置以进行测试,测试,拆卸等.此外,从Go 1.5开始,测试可以同时运行,因此您甚至需要放置一个互斥锁.如果这样保留程序包,请锁定全局DB变量.切换到IoC模式(例如我在上面的代码中演示的Dependency Injection),使测试(和调试)代码变得更加容易.

Also note that your code in other packages now becomes easily testable by creating a mock sql.DB object and injecting it into NewStatsEngine, getUsers, etc instead of having to mock a global var, test, tear down and setup again for test test, test, teardown, etc. Also since Go 1.5, tests could be run concurrently so you'll even need to put a mutex.Lock around your global DB var if you leave your package like that. Switching to a IoC pattern, like Dependency Injection which I demo'd in the code above, makes so much easier to test (and debug) your code.

这篇关于Go-变量已初始化且非nil,但对于其他函数则为nil的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆