使用sql.DB

时间:2016-11-04 11:12:05

标签: mysql go

我正在学习Go,并尝试制作一个小的SQL工具集:

type DBUtils struct {
  User string
  Password string
  Host string
  Database string
  Handle *sql.DB
}

func (dbUtil DBUtils) Connect() {
  var err error
  dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "@tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
  if err != nil {
    panic(err.Error()) 
  }

  err = dbUtil.Handle.Ping()
  if err != nil {
    panic(err.Error())
  }

  fmt.Printf("%v", dbUtil)
}

func (dbUtil DBUtils) Close() {
  dbUtil.Handle.Close()
}

func (dbUtil DBUtils) GetString(what string, from string, where string, wherevalue string) string {
  var username string

  fmt.Printf("%v", dbUtil)

  stmtOut, err := dbUtil.Handle.Prepare("SELECT " + what + " FROM " + from + " WHERE " + where + " = " + wherevalue)
  if err != nil {
      panic(err.Error()) // proper error handling instead of panic in your app
  }

  err = stmtOut.QueryRow(1).Scan(&username)

  return username
}

所以当使用以下代码时:

db := databaseutils.DBUtils{"root", "root", "127.0.0.1:3306", "gotest", nil}    
db.Connect() // I get: {root root 127.0.0.1:3306 gotest 0xc42019d600}
fmt.Printf("%v", db) // I get {root root 127.0.0.1:3306 gotest <nil>}
x := db.GetString("username", "users", "id", "1") // Doesn't work: panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(x)

对我而言,似乎我的数据库句柄未正确保存?有没有人有一个想法 - 我很陌生,有很多东西看起来与PHP,JS,C ++等不同。

提前致谢!

2 个答案:

答案 0 :(得分:2)

您的Connect方法不会更改您正在调用方法的对象的状态。你正在处理这种类型的副本。如果你想要一个方法来改变对象本身,你必须在指针上定义它:

func (dbUtil *DBUtils) Connect() {
//instead of
func (dbUtil DBUtils) Connect() {

如果您熟悉C或C ++,那么您当前的方法与以下方法类似:

void connect_db(struct db_utils db_util)
{}

当您调用这样的函数时,您将创建参数的副本,并将其推送到堆栈。 <{1}}函数可以使用它,在它返回后,副本将被释放。

将其与此C代码进行比较:

connect_db

同样的事情发生在go。定义方法的对象只有在有权访问实例时才能更改实例的状态。如果没有,它就无法更新内存的那一部分。

如果您想知道,不需要在指针类型上定义此方法:

struct foo {
    int bar;
};
static
void change_copy(struct foo bar)
{
    bar.bar *= 2;
}

static
void change_ptr(struct foo *bar)
{
    bar->bar *= 2;
}

int main ( void )
{
    struct foo bar = {10};
    printf("%d\n", bar.bar);//prints 10
    change_copy(bar);//pass by value
    printf("%d\n", bar.bar);//still prints 10
    change_ptr(&bar);
    printf("%d\n", bar.bar);//prints 20
    return 0;
}

这是func (dbUtil DBUtils) Close() { dbUtil.Handle.Close() } 是指针类型的原因。该指针的副本将始终指向同一资源。

我会说这个,但是:假设您实际上是在处理句柄,并且您正在公开DBUtils.HandleConnect方法,那么该成员本身实际上不应该被导出。我将其更改为小写Close

答案 1 :(得分:0)

你的DB句柄没有被保存是对的。您正在值接收器上定义方法。因此,您会在Connect方法中收到副本并修改所述副本。然后在Connect方法的末尾删除此副本。

您必须在指向结构的指针上定义方法:

func (dbUtil *DBUtils) Connect() {
  var err error
  dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "@tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
  if err != nil {
    panic(err.Error()) 
  }

  err = dbUtil.Handle.Ping()
  if err != nil {
    panic(err.Error())
  }

  fmt.Printf("%v", dbUtil)
}

有关详细信息,请参阅https://golang.org/doc/faq#methods_on_values_or_pointers

注意:我注意到您使用了QueryRow。它接受prepare语句中参数的参数。你没有参数,所以我认为你不应该传递任何参数。您还应该检查Scan结果是否有错误(请参阅https://golang.org/pkg/database/sql/#Stmt)。