我正在学习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 ++等不同。
提前致谢!
答案 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.Handle
和Connect
方法,那么该成员本身实际上不应该被导出。我将其更改为小写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)。