安装驱动#
go中database/sql
已封装好了完整的sql使用API,但没有驱动,需要导入对应数据库的驱动。
在这里以mysql为例,Go MySQL Driver是 Godatabase/sql/driver
接口的一个实现,只需导入驱动程序即可。
在项目demo终端下载驱动:
go get -u github.com/go-sql-driver/mysql
连接数据库#
原生包中func Open(driverName, dataSourceName string ) (* DB , error )
函数打开数据库,返回一个DB对象,DB 是一个数据库句柄,表示一个包含零个或多个底层连接的池。多个 goroutine 并发使用它是安全的。。
其中dervierName
指定数据库名称,dataSourceName
为数据源名称(DSN)。
DSN其格式为username:password@protocol(address)/dbname?param=value
,其中dbname
为要连接的库名字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
//创建全局db对象
var db *sql.DB
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/sys?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
//ping为测试是否连通,确保真正连接上
err = db.Ping()
if err != nil {
fmt.Printf("connect failed,err:%v", err)
return
}
return
}
func main() {
err := initDB()
if err != nil {
fmt.Printf("init db failed,err:%v\n", err)
return
}
defer db.Close()
}
|
基本使用#
通过创建user
表,同以下结构,进行CRUD
创建user
结构体
1
2
3
4
5
|
type user struct {
id int
name string
age int
}
|
Query#
查询一行#
func (db * DB ) QueryRow(query string , args ... any ) * Row
QueryRow
执行最多返回一行的查询。QueryRow
总是返回一个非零值。错误会延迟到调用 Row
的 Scan
方法时出现。
1
2
3
4
5
6
7
8
9
10
11
|
func queryRow() {
var u1 user
queryStr := "select * from user where id = ?"
row := db.QueryRow(queryStr, 1)
err := row.Scan(&u1.id, &u1.name, &u1.age)
if err != nil {
fmt.Printf("queryRow error: %v", err)
return
}
fmt.Println(u1)
}
|
查询多行#
func (db * DB ) Query(query string , args ... any ) (* Rows , error )
Query
执行返回多行的查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func query() {
userSlice := make([]user, 0, 2)
rows, _ := db.Query("select * from user")
defer rows.Close() //在这里调用close是因为不确定scan时是否会出错,如出错,而rows又会一直占用连接,此时应该关闭
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("query error: %v", err)
continue
}
userSlice = append(userSlice, u)
}
fmt.Println(userSlice)
}
|
Exec#
Exec
执行查询而不返回任何行,插入、更新和删除操作都使用它。
Exec
返回一个Result
实例
1
2
3
4
5
6
|
type Result interface {
// LastInsertId 返回数据库生成的整数,即为自增的id。并非所有数据库都支持此功能,并且此类语句的语法各不相同
LastInsertId() ( int64 , error )
// RowsAffected 返回受 更新、插入或删除影响的行数。并非每个数据库或数据库驱动程序都支持这一点。
RowsAffected() ( int64 ,错误)
}
|
1
2
3
4
5
6
7
8
9
10
11
|
func execInsert() {
insertSql := "insert into user(name,age) values(?,?)"
insertRes, err := db.Exec(insertSql, "汪汪汪汪", 24)
if err != nil {
fmt.Printf("insert failed, err:%v", err)
return
}
insertId, _ := insertRes.LastInsertId() //最后插入的id
rowsAffected, _ := insertRes.RowsAffected() //影响的行数
fmt.Println(insertId, rowsAffected)
}
|
1
2
3
4
5
6
7
8
9
10
|
func execUpdate() {
updateSql := "update user set age = ? where id = ?"
res, err := db.Exec(updateSql, 18, 3)
if err != nil {
fmt.Printf("update failed, err:%v", err)
return
}
rowsAffected, _ := res.RowsAffected()
fmt.Println(rowsAffected)
}
|
1
2
3
4
5
6
7
8
9
10
|
func execDelete() {
delectSql := "delete from user where id = ?"
res, err := db.Exec(delectSql, 1)
if err != nil {
fmt.Printf("delete failed, err:%v", err)
return
}
rowsAffected, _ := res.RowsAffected()
fmt.Println(rowsAffected)
}
|
预处理#
在以上这些CRUD中,使用的都是即时SQL
,数据库接收到一条sql后,其执行流程大致如下:
1.词法和语义解析
2.优化 SQL 语句,制定执行计划
3.执行并返回结果
一次编译,一次运行,而大部分情况下,一条sql其实是一直被反复运行的,如单纯的查询操作,又或者是sql中的参数不同,其他都一样,如果每次都要经历这样的流程去解析,无疑会降低效率。
预处理通过将一般sql语句模版化,做到一次编译,多次运行,提高效率。
使用下面函数来进行预处理
func (db * DB ) Prepare(query string ) (* Stmt , error )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func prepare() {
stmt, err := db.Prepare("select * from user where id = ?")
if err != nil {
fmt.Printf("exec Prepare failed, err:%v", err)
return
}
var u user
err = stmt.QueryRow(4).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("queryRow err:%v", err)
return
}
fmt.Println(u)
}
|
其他的操作也大同小异。
在sql
包中,有3个关于事务的主要方法。
func (db *DB) Begin() (*Tx, error)
:开启事务
func (tx *Tx) Commit() error
:提交事务
func (tx *Tx) Rollback() error
:回滚事务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
func tx() {
tx, err := db.Begin()
if err != nil {
fmt.Printf("tx begin error : %v", err)
return
}
updateSql := "update user set age = ? where id = ?"
_, err = tx.Exec(updateSql, 38, 2)
if err != nil {
fmt.Printf("error:%v", err)
tx.Rollback()
return
}
_, err = tx.Exec(updateSql, 29, 4)
if err != nil {
fmt.Printf("error:%v", err)
tx.Rollback()
return
}
tx.Commit()
fmt.Println("ok")
}
|
加入预处理的事务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
func txPrepare() {
tx, err := db.Begin()
if err != nil {
fmt.Printf("tx begin error : %v", err)
return
}
stmt, _ := tx.Prepare("update user set age = ? where id = ?")
_, err = stmt.Exec(28, 2)
if err != nil {
fmt.Printf("error:%v", err)
tx.Rollback()
return
}
_, err = stmt.Exec(22, 4)
if err != nil {
fmt.Printf("error:%v", err)
tx.Rollback()
return
}
tx.Commit()
fmt.Println("ok")
}
|