问题:使用gin框架查询mysql中一行数据报错
- 报错内容:panic: runtime error: invalid memory address or nil pointer dereference
- 代码如下:
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" // init "time" ) var db *sql.DB func initMySQL() (err error) { // DSN:Data Source Name dsn := "root:yonbip2021!@tcp(127.0.0.1:3307)/sql_demo" // 去初始化全局的db对象,而不是新声明一个db变量 db, err := sql.Open("mysql", dsn) if err != nil { panic(err) } // 尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { fmt.Printf("connect to db failed, err: %v\n", err) return } // 数值需要业务具体情况来确定 db.SetConnMaxLifetime(time.Second * 10) // db.SetMaxOpenConns(200) //最大连接数 db.SetMaxIdleConns(10) // 最大空闲连接数 return } type user struct { id int age int name string } // 查询单条数据示例 func queryRowDemo() { sqlStr := "select id, name, age from user where id=?" var U user // 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放 err := db.QueryRow(sqlStr, 1).Scan(&U.id, &U.name, &U.age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("id:%d name:%s age:%d\n", U.id, U.name, U.age) } func main() { if err := initMySQL();err != nil { fmt.Printf("connect to db failed, err:%v\n", err) } // Close()用来释放掉数据库连接相关的资源 // 做完错误检查之后,确保db不为nil defer db.Close() // 注意这行代码要写在上面err判断的下面 fmt.Println("connect to db success") // db.xx() 去使用数据库操作 queryRowDemo() }
排查过程:
-
报错内容中已经打印出“connect to db success”,证明在59行以前,代码是没有问题的。所以我在61行打了断点,查看queryRowDemo函数的具体执行过程
-
当debug进行到queryRowDemo函数的底44行时,问题出现了,db(指针类型的问题接收者)的值为nil,但是可以看到在第10行定义了一个全局的sql.DB类型db变量,照理说不应该为nil才对,初步猜测是在哪里被覆盖了。
-
查看在queryRowDemo()函数之前执行的initMysql()函数,有一行代码(16行)引起了我的注意
db, err := sql.Open("mysql", dsn)
-
这行代码对db重新做了初始化(:=),而不是赋值(=),到这里为什么db为nil就有了结论。
-
将16行代码的":=“改为”="后重新执行,运行成功:
总结: -
go语言中=与:=分别表示赋值和初始化,使用的时候要注意不要写错
-
看不出代码问题的时候熟练使用debug能帮助你快速解决问题