75142913在线留言
GO web笔记3:使用GO database/sql对数据库的基本操作_Go语言_网络人

GO web笔记3:使用GO database/sql对数据库的基本操作

Kwok 发表于:2020-11-19 09:19:20 点击:51 评论: 0

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数据库等。

go-sql-driver的安装使用

安装和其它包一样,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数据库mysqlKwok最后编辑于:2020-11-19 12:19:34
0
感谢打赏!

《GO web笔记3:使用GO database/sql对数据库的基本操作》的网友评论(0)

本站推荐阅读

热门点击文章