GO语言与MySQL的结合还是比较容易的,像是连接,增、删、改这些操作都比较简单,唯独在查询的时候感觉比较坑(见代码),下面是官方推荐的标准查询方式:
var id int
var name string
var address string
rows, err := DB.Query("select id,name,address from users")
if err != nil {
fmt.Println(err)
}
for rows.Next() {
rows.Scan(&id, &name, &address)
fmt.Println(id, name, address)
}
defer rows.Close()
这种正统的方法在执行SQL语句后,还需要通过rows.Scan()逐行读出数据,最关键的是还需要与咱的SQL语句配合好,语句里面查询了几个列,Scan这边就必须相应地用同样的独立变量与之一 一对应,否则就会报错。
查询功能是数据库应用中用得最频繁的,各种不同的表,列的数量和列名千差万别,按照上面这种查询模式,那么每一种查询那都得要重新写一个读取函数,感觉这非常浪费时间,因此,一种通用的可变列名和列数的查询方法就显得非常重要了。
以下是完整代码(我做了详细注释,很容易明白):
package mysql_con
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
var DB *sql.DB
func initDB() bool { //连接到MySQL
path := "root:password@tcp(127.0.0.1:3306)/mydb?charset=utf8"
//root = 用户名
//password = 密码
//mydb = 数据库名称
fmt.Println(path)
DB, _ = sql.Open("mysql", path)
DB.SetConnMaxLifetime(100)
DB.SetMaxIdleConns(10)
//验证连接
if err := DB.Ping(); err != nil {
return false
}
return true
}
func Exec(SQL string) {
if initDB() == true {
ret, _ := DB.Exec(SQL) //增、删、改就靠这一条命令就够了,很简单
insID, _ := ret.LastInsertId()
fmt.Println(insID)
}
}
func Query(SQL string) ([]map[string]string, bool) { //通用查询寒素
if initDB() != true { //连接数据库
return nil, false
}
rows, err := DB.Query(SQL) //执行SQL语句,比如select * from users
if err != nil {
panic(err)
}
columns, _ := rows.Columns() //获取列的信息
count := len(columns) //列的数量
var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
for i, _ := range values {
var ii interface{} //为空接口分配内存
values[i] = &ii //取得这些内存的指针,因后继的Scan函数只接受指针
}
ret := make([]map[string]string, 0) //创建返回值:不定长的map类型切片
for rows.Next() {
err := rows.Scan(values...) //开始读行,Scan函数只接受指针变量
m := make(map[string]string) //用于存放1列的 [键/值] 对
if err != nil {
panic(err)
}
for i, colName := range columns {
var raw_value = *(values[i].(*interface{})) //读出raw数据,类型为byte
b, _ := raw_value.([]byte)
v := string(b) //将raw数据转换成字符串
m[colName] = v //colName是键,v是值
}
ret = append(ret, m) //将单行所有列的键值对附加在总的返回值上(以行为单位)
}
defer rows.Close()
if len(ret) != 0 {
return ret, true
}
return nil, false
}
至此我们的通用Query查询函数就大功告成了,这个函数在使用起来也比较简单:
rst, ok := mysql_con.Query("select * from users where id = 1")
//rst是返回的记录集,ok表示是否查询成功,也可以用来判断是不是空查询
if ok {
fmt.Println(rst[0]["name"])
} else {
fmt.Println("没有找到数据")
}
使用的时候就用rst[行] ["列名"]来读取数据即可,一个函数可以对付所有的场景,感觉非常巧妙!
PS:本文参阅了以下文章,并进行了一些小的改动:
https://stackoverflow.com/questions/17845619/how-to-call-the-scan-variadic-function-using-reflection