0/参考网址
blog.csdn.net/weixin_5379…
juejin.cn/post/684490…
1/前言
go语言中自带的database/sql库包提供了保证sql或者类sql数据库的泛用接口,并没有提供具体的数据库驱动.
也就是说我们在使用database/sql库包的时候,必须注入至少一个数据库驱动。
也就是说,database/sql这个go内置的库包,必须和具有数据驱动功能的第三方库包一起使用。
我们常用的数据库基本上都有完整的第三方实现,例如“driver”可以理解为驱动
github.com/go-sql-driver/mysql
2/下载依赖的库包
通过以下命令,安装依赖的库包
go get github.com/go-sql-driver/mysql
3/连接数据库,初始化客户端
下面这一段代码,就是要创建一个可以操作mysql数据库的客户端client,也可以理解为是一个具柄。
有了这个具柄,就可以去执行·增删改查·这些操作了
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var mysql_client *sql.DB
func InitMysqlDB() (err error) {
dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
mysql_client, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
err = mysql_client.Ping()
if err != nil {
return err
}
return nil
}
func main() {
err := InitMysqlDB()
if err != nil {
fmt.Printf("init db failed,err:%v\n", err)
return
}
}
4/CRUD(create,read,update,delete)
操作数据库
1/创建库,创建表
这个操作不是通过go代码来实现的,而是直接在mysql中,通过mysql的命令来实现的
我们先在MySQL中创建一个名为sql_test的数据库
CREATE DATABASE sql_test;
然后进入该数据库:
use sql_test;
执行以下命令创建一张用于测试的数据表user_table
该表中有3个字段,id,name,age
其中id这个字段是唯一键,是不能重复的。再往里写数据的时候,如果id重复,就会报错。
CREATE TABLE `user_table` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT '',
`age` INT(11) DEFAULT '0',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
2/查询数据
查询数据,最后返回结果。
为了查询方便,我们事先定义一个结构体,来存储查返回的数据
type user struct {
id int
name string
age int
}
<1>单行查询
单行查询`db_client.QueryRow()`执行一次查询,并期望返回最多一行结果(即Row)。
QueryRow总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。
具体的代码示例:
func queryRowDemo() {
sql_command := "select id, name, age from user_table where id=?"
var u user
err := mysql_client.QueryRow(sql_command, 1).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
log.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
<2>多行查询-query
多行查询`mysql_client.Query()`执行一次查询,返回多行结果(即Rows),
一般用于执行select命令。参数args表示query中的占位参数。
具体代码示例:
func queryMultiRowDemo() {
sql_command := "select id, name, age from user where id > ?"
rows, err := db.Query(sql_command, 0)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return
}
defer rows.Close()
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Println("scan failed, err:", err)
return
}
fmt.Println(u.id, u.name, u.age)
}
}
3/插入数据-insert
插入、更新和删除操作都使用`Exec`方法。
Exec执行一次命令(包括查询、删除、更新、插入等),返回的Result是对已执行的SQL命令的总结。
参数args表示query中的占位参数。
func insertRowDemo() {
sql_command := "insert into user(name, age) values (?,?)"
ret, err := mysql_client.Exec(sql_command, "王五", 38)
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return
}
theID, err := ret.LastInsertId()
if err != nil {
fmt.Printf("get lastinsert ID failed, err:%v\n", err)
return
}
fmt.Printf("insert success, the id is %d.\n", theID)
}
4/更新数据-update
func updateRowDemo() {
sql_command := "update user set age=? where id = ?"
ret, err := mysql_client.Exec(sql_command, 39, 3)
if err != nil {
fmt.Printf("update failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected()
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("update success, affected rows:%d\n", n)
}
5/删除数据
func deleteRowDemo() {
sql_command := "delete from user where id = ?"
ret, err := mysql_client.Exec(sql_command, 3)
if err != nil {
fmt.Printf("delete failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected()
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("delete success, affected rows:%d\n", n)
}
事务
一个最小的不可再分的工作单元,就是事务。
通常一个事务,对应一个完成的业务,比如银行转账业务,该业务就是一个最小的工作单元。
这个完整的业务需要执行多次的DML(insert, update,delete)语句共同联合完成。
在MySQL中只有使用了`Innodb`数据库引擎的数据库或表才支持事务。
事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行。
mysql单条数据查询并返回结果(将 sql.Rows 转化为 map )
这里的sql.Rows就是查询单条数据的函数,然后返回的结果。
因为查询的是单条数据,所以返回值的类是map[string]string,及key-value的形式,
func GetResultRow(rows *sql.Rows) map[string]string{
columns, _ := rows.Columns()
scanArgs := make([]interface{}, len(columns))
values := make([]interface{}, len(columns))
for i := range values {
scanArgs[i] = &values[i]
}
record := make(map[string]string)
for rows.Next() {
rows.Scan(scanArgs...)
for i, v := range values {
if v != nil {
record[columns[i]] = string(v.([]byte))
}
}
}
return record
}
查询多条数据,并返回结果(将 sql.Rows 转化为 map[int]map[string]string)
int是行号,因为返回的是多行数据,所以需要有行号。
这就是相当于在原来key-value的形式下,在外面又包装了一层key。
{1:{"id":"1","name":"zhangsan","age":"56"},
2:{"id":"1","name":"lisi","age":"50"},
3:{"id":"1","name":"wangwu","age":"70"},
......
......}
func GetResultRows(rows *sql.Rows) map[int]map[string]string {
columns, _ := rows.Columns()
vals := make([][]byte, len(columns))
scans := make([]interface{}, len(columns))
for k, _ := range vals {
scan[k] = &val[k]
}
i := 0
result := make(map[int]map[string]string)
for row.Next() {
rows.scan(scans...)
row := make(map[string]string)
for k, v := range vals {
key := columns[k]
row[key] = string(v)
}
result[i] = row
i++
}
return result
}