简介
本篇主要实践Golang采用官方驱动连接MySQL, 以及执行原生SQL语句和解析, 解析过程中不使用结构体.
核心原理图:
完整代码:
package mainimport ( "database/sql" "encoding/json" "fmt" _ "github.com/go-sql-driver/mysql" //被database/sql包使用的MYSQL驱动, 官方文档和项目:https://github.com/go-sql-driver/mysql "log" "time")var ( Db *sql.DB err error)//获取数据库控制器func GetDb(host string, port int, username string, password string) (*sql.DB, error) { //DSN (Data Source Name)数据源连接格式:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] //这里我们可以不选择数据库,或者增加可选参数,比如timeout(建立连接超时时间) //mysqlConnStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?&charset=utf8&parseTime=True&loc=Local&timeout=5s", username, password, host, port) dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=5s", username, password, host, port) Db, err = sql.Open("mysql", dsn) if err != nil { log.Printf("配置连接出错:%s", err.Error()) return Db, err } // 设置连接池中空闲连接的最大数量。 Db.SetMaxIdleConns(1) // 设置打开数据库连接的最大数量。 Db.SetMaxOpenConns(1) // 设置连接可复用的最大时间。 Db.SetConnMaxLifetime(time.Second * 30) //设置连接最大空闲时间 Db.SetConnMaxIdleTime(time.Second * 30) //检查连通性 err = Db.Ping() if err != nil { log.Printf("数据库连接出错:%s", err.Error()) return Db, err } return Db, err}//单行数据解析 查询数据库,解析查询结果,支持动态行数解析func QueryAndParse(Db *sql.DB, queryStr string) map[string]string { rows, err := Db.Query(queryStr) defer rows.Close() if err != nil { log.Printf("查询出错,SQL语句:%s错误详情:%s", queryStr, err.Error()) return nil } //获取列名cols cols, _ := rows.Columns() if len(cols) > 0 { buff := make([]interface{}, len(cols)) // 创建临时切片buff data := make([][]byte, len(cols)) // 创建存储数据的字节切片2维数组data dataKv := make(map[string]string, len(cols)) //创建dataKv, 键值对的map对象 for i, _ := range buff { buff[i] = &data[i] //将字节切片地址赋值给临时切片,这样data才是真正存放数据 } for rows.Next() { rows.Scan(buff...) // ...是必须的,表示切片 } for k, col := range data { dataKv[cols[k]] = string(col) //fmt.Printf("%30s:%s", cols[k], col) } return dataKv } else { return nil }}//多行数据解析func QueryAndParseRows(Db *sql.DB, queryStr string) []map[string]string { rows, err := Db.Query(queryStr) defer rows.Close() if err != nil { fmt.Printf("查询出错:SQL:%s, 错误详情:%s", queryStr, err.Error()) return nil } //获取列名cols cols, _ := rows.Columns() if len(cols) > 0 { var ret []map[string]string for rows.Next() { buff := make([]interface{}, len(cols)) data := make([][]byte, len(cols)) //数据库中的NULL值可以扫描到字节中 for i, _ := range buff { buff[i] = &data[i] } rows.Scan(buff...) //扫描到buff接口中,实际是字符串类型data中 //将每一行数据存放到数组中 dataKv := make(map[string]string, len(cols)) for k, col := range data { //k是index,col是对应的值 //fmt.Printf("%30s:%s", cols[k], col) dataKv[cols[k]] = string(col) } ret = append(ret, dataKv) } return ret } else { return nil }}//任意可序列化数据转为Json,便于查看func Data2Json(anyData interface{}) string { JsonByte, err := json.Marshal(anyData) if err != nil { log.Printf("数据序列化为json出错:%s", err.Error()) } return string(JsonByte)}func main() { //获取数据库控制器DB DB, err := GetDb("data", 3306, "root", "root") if err != nil { log.Printf("获取数据库控制器出错:%s", err.Error()) } defer DB.Close() //延迟关闭数据库控制器,释放数据库连接 //单行数据查询 showMasterStatus := QueryAndParse(Db, "show master status") log.Printf("单行数据-数据库状态:%v", Data2Json(showMasterStatus)) log.Printf("单行数据-数据库状态-File:%v", showMasterStatus["File"]) //多行数据查询 showProcessList := QueryAndParseRows(Db, "show processlist") log.Printf("多行数据-进程信息:%v", Data2Json(showProcessList)) log.Printf("多行数据-进程信息-Host:%v", showProcessList[0]["Host"])}
执行结果:
2020/11/03 21:50:08 单行数据-数据库状态:{"Binlog_Do_DB":"","Binlog_Ignore_DB":"","Executed_Gtid_Set":"","File":"mysql-bin.000002","Position":"11543900"}2020/11/03 21:50:08 单行数据-数据库状态-File:mysql-bin.0000022020/11/03 21:50:08 多行数据-进程信息:[{"Command":"Query","Host":"10.254.102.110:51582","Id":"17814","Info":"show processlist","State":"starting","Time":"0","User":"roo""}]2020/11/03 21:50:08 多行数据-进程信息-Host:10.254.102.110:51582
解释说明:
- main函数开始执行,首先获取数据库控制器DBDB, err := GetDb("data", 3306, "root", "root")主要设置数据源连接信息, 连接池配置,联通性检测等//获取数据库控制器func GetDb(host string, port int, username string, password string)
• (*sql.DB, error) {//DSN (Data Source Name)数据源连接格式:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
//这里我们可以不选择数据库,或者增加可选参数,比如timeout(建立连接超时时间)
//mysqlConnStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?&charset=utf8&parseTime=True&loc=Local&timeout=5s", username, password, host, port)dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=5s", username, password, host, port)
Db, err = sql.Open("mysql", dsn)if err != nil {log.Printf("配置连接出错:%s", err.Error())return Db, err
}// 设置连接池中空闲连接的最大数量。Db.SetMaxIdleConns(1)// 设置打开数据库连接的最大数量。Db.SetMaxOpenConns(1)// 设置连接可复用的最大时间。Db.SetConnMaxLifetime(time.Second * 30)//设置连接最大空闲时间Db.SetConnMaxIdleTime(time.Second * 30)//检查连通性err = Db.Ping()if err != nil {log.Printf("数据库连接出错:%s", err.Error())return Db, err
}return Db, err
}
- 接着执行查询语句show master status,并解析查询结果,得到Map对象,便于直接取值. 这里没有使用结构体来解析,所以对于结果中包含多列字段的场景非常友好!//单行数据解析 查询数据库,解析查询结果,支持动态行数解析
• func QueryAndParse(Db *sql.DB, queryStr string) map[string]string {
rows, err := Db.Query(queryStr)defer rows.Close()if err != nil {log.Printf("查询出错,SQL语句:%s错误详情:%s", queryStr, err.Error())return nil}//获取列名colscols, _ := rows.Columns()if len(cols) > 0 {
buff := make([]interface{}, len(cols)) // 创建临时切片buffdata := make([][]byte, len(cols)) // 创建存储数据的字节切片2维数组datadataKv := make(map[string]string, len(cols)) //创建dataKv, 键值对的map对象for i, _ := range buff {
buff[i] = &data[i] //将字节切片地址赋值给临时切片,这样data才是真正存放数据}for rows.Next() {
rows.Scan(buff...) // ...是必须的,表示切片}for k, col := range data {
dataKv[cols[k]] = string(col)//fmt.Printf("%30s:%s", cols[k], col)}return dataKv
} else {return nil}
}
- 如果查询语句返回多行结果,与单行类似,只是返回map切片即可
参考文档:
https://github.com/go-sql-driver/mysql
END已结束