GO语言官方只提供了数据库操作的接口,并未提供各类数据库的驱动,我们如果要对mysql数据库CRUD(R增、R查、U改、D删除)操作就需要使用到第三方的驱动,官方推荐页:https://github.com/golang/go/wiki/SQLDrivers 这里我们针对Mysql的驱动是 MySQL: https://github.com/go-sql-driver/mysql/。介绍也很精简:A MySQL-Driver for Go's database/sql package.
截止发表这篇笔记,上次更新时间为5天前。go-sql-driver的特点是:
1、轻巧并且速度快,而且使用了纯GO语言没有C绑定,导入包的时候只需要使用init(即引包加“_”)。
2、支持IPV6、IPV4,Unix 域套接字,或者自己定义的协议。
3、自由维护mysql的连接池,我们只管连接,不用关闭(使用database/sql 自动连接池)。
4、完全支持sql.RawBytes及大于16MB的查询。
5、支持io.Reader、time.Time解析、预查询占位等诸多特性。
go-sql-driver要求GO 1.10 以上版本及MYSQL4.1以上MariaDB数据库等。
安装和其它包一样,1行命令即可导出到$GOPATH目录下。
$ go get -u github.com/go-sql-driver/mysql
当然本地需要GIT的支持,还有GOPATH配置要正确的前提下。
database/sql/driver的实现,只需要导入驱动程序,然后就可以使用完整的database/sql API,下面用代码演示对mysql的数据库基本操作CRUD,只需要链接好数据库,会自动完成对数据表的创建、插入、查询操作。
import (
"database/sql"
"fmt"
"strconv"
"time"
_ "github.com/go-sql-driver/mysql" //MySql驱动包,这里"_"只需要执行一下这个包的init函数
)
var (
Db *sql.DB //定义一个sql包里的DB指针
err error //定义一个错误类型
dbHost = "127.0.0.1" //数据库主机
dbPort = "3306" //数据库端口
dbName = "test" //数据库名
dbUser = "gotest" //数据库用户名
dbPwd = "123..123" //数据库密码
sqlStr = dbUser + ":" + dbPwd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
//sqlStr = "gotest:123..123@/test"//[tcp(127.0.0.1:3306)]是可选参数,如果是默认端口&&本地IP,这里就可以省略。
)
func init() {
Db, err = sql.Open("mysql", sqlStr) //Open文件只验证连接字符是否合法
if err != nil {
panic("数据连接字符验证失败:" + err.Error())
}
err = Db.Ping() //检测数据库是否连接成功
if err != nil {
panic("数据库未连接成功:" + err.Error())
}
Db.SetConnMaxLifetime(time.Minute * 3) //设置连接最大活跃时间
Db.SetMaxOpenConns(10) //设置连接最大打开数
Db.SetMaxIdleConns(10) //设置连接最大闲置数
}
func main() {
//1、使用GO创建一个表
tableName := "go_user" //表名
sql := "CREATE TABLE IF NOT EXISTS `" + tableName + "` (" +
"`id` INT(8) UNSIGNED NOT NULL AUTO_INCREMENT ," +
"`name` VARCHAR(255) NOT NULL ," +
"`sex` TINYINT(1) UNSIGNED NOT NULL ," +
"`age` TINYINT UNSIGNED NOT NULL ," +
"PRIMARY KEY (`id`)," +
"INDEX `name` (`name`)" +
")" +
"ENGINE = InnoDB " +
"CHARSET=utf8mb4 " +
"COLLATE utf8mb4_general_ci " +
"COMMENT = '测试使用GO创建一个" + tableName + "表';"
_, err = Db.Exec(sql) //直接运行sql语句,当使用删除、更新不需要返回结果的都可以使用Exec运行即可
//这里被忽略的Result.RowsAffected()返回被update、insert或delete命令影响的行数
if err != nil {
fmt.Println("Db.Exec执行SQL语句出错:", err)
} else {
fmt.Println(tableName, "表创建成功,或者已存在~")
}
var lastID int64 //用于保存写入的最新ID,LastInsertId返回int64
//2、向表里插入数据,预查询方式
sql = "INSERT INTO `" + tableName + "` (`name`, `sex`, `age`) VALUES (?,?,?)"
insPre, err := Db.Prepare(sql) //预查询
if err != nil {
fmt.Println("预查询出现异常:", err)
}
Result, err := insPre.Exec("张三", "0", "18") //填充预查询并执行SQL语句
if err != nil {
fmt.Println("insPre插入数据出错:", err)
} else {
//Result.LastInsertId()当插入新行时返回一个数据库"自增"生成int64。
lastID, _ = Result.LastInsertId()
fmt.Println("预查询新插入ID:", lastID)
}
//不使用预查询直接运行的方式
Result, err = Db.Exec(sql, "李四", "0", "19")
if err != nil {
fmt.Println("Db.Exec运行出错:", err)
} else {
//Result.LastInsertId()当插入新行时返回一个数据库"自增"生成int64。
lastID, _ = Result.LastInsertId()
fmt.Println("Db.Exec插入ID:", lastID)
}
//3、查询上面插入的最新数据
sql = "SELECT `id`,`name`,`sex`,`age` FROM `" + tableName + "` WHERE `id`=" + strconv.FormatInt(lastID, 10)
row := Db.QueryRow(sql) //将查询最多只能返回一行给row,错误被推迟到行的Scan方法被调用
var id int //接收`id`
var name string //接收`name`
var sex byte //接收`sex`
var age byte //接收`age`
err = row.Scan(&id, &name, &sex, &age)
if err != nil {
fmt.Println("row.Scan出错:", err, "nSQL:", sql)
} else {
sexName := "女"
if sex == 0 {
sexName = "男"
}
fmt.Println("=============> 最新插入的数据为 <=============")
fmt.Printf("nID:%d 姓名:%v 姓别:%v 年龄:%vnn", id, name, sexName, age)
}
//查询并返回多行数据
fmt.Println("=============> 测试按条件查询并返回多条记录 <=============")
sql = "SELECT * FROM `" + tableName + "` WHERE `id` in(?,?,?)"
rows, err := Db.Query(sql, "1", "2", "3") //Query执行返回满足条件所有行的SELECT查询
if err != nil {
fmt.Println("Db.Query查询出错:", err, "nSQL:", sql)
}
colName, _ := rows.Columns() //Columns返回列名。如果行关闭,列将返回错误。
//fmt.Println(colName) //[]string{id name sex age}
userCol := make([][]byte, len(colName)) //接收列名字到切片
scans := make([]interface{}, len(userCol)) //接收Scan到的值
for k := range userCol {
scans[k] = &userCol[k] //数据转移到scans,scans[0]="id",scans[0]
}
i := 0
users := make(map[int]map[string]string) //定义一个map用于存储所有查询结果
for rows.Next() {
rows.Scan(scans...) //使用scans切片接收填充数据
theRow := make(map[string]string) //接收当前Scan到的1行数据到map切片
for k, v := range userCol {
theRow[colName[k]] = string(v) //这里把[]byte数据转成string
}
users[i] = theRow //把格式化后的theRow放入users结果集
i++
}
for _, v := range users {
fmt.Printf("ID:%v 名字:%v 性别:%v 年龄:%vn", v["id"], v["name"], v["sex"], v["age"])
}
}
这里特别说明一下 Db.Exec 返回的Result会有2个结果,.LastInsertId()返回刚才插入的自增ID及.RowsAffected()多少行被update、insert或delete命令所影响。LastInsertId将会在关系数据库里大量所使用。
这里只演示sql查询的常用方法,在实际的项目开发中我们需要把sql的一系列操作封装成工具包所使用,不会写这么繁琐的代码。当然最好用的还是ORM框架对数据库的操作。在下一篇文章里我们会详细介绍使用XORM对关系型数据库的操作。
除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/94
《GO web笔记3:使用GO database/sql对数据库的基本操作》的网友评论(0)