go访问数据库

TODO 背景

记录一些go访问数据库的方式

go的方式

官方仅仅提供了接口,规定了一些需要有的方法,并归类在接口中.
都是很常见的一些特性.

  1. sql.DB 数据库的连接 (Exec, QueryRow, Query, Prepare, Begin)
  2. sql.Stmt 模板话的语句 (Exec, QueryRow, Query)
  3. sql.Tx 事务对象 (Commit, Rollback, Exec) (Query也可,估计是为了保证tx被封装在函数里之后,还能Query)
  4. sql.Result 执行结果 (LastInsertId, RowsAffected)
  5. sql.Rows 查询结果集 (Next, Close, Scan)
  6. sql.Row 查询结果,单条 (Scan)

而具体的驱动,由第三方提供.
方式上

  1. 官方提供 driver.Driver 类, 并提供 sql.Register(name, &driver) 的方法用于注册驱动
  2. 第三方库在自己的init函数中,调用该方法,注册自己的驱动.
  3. 注册好的驱动保存在一个数组drivers中
  4. 当调用 sql.Open(name, str) 时,再从drivers中取出对应的driver,用于建立连接

以mysql为例

go-sql-driver/mysql 提供了依然还在更新的driver.

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import (
"database/sql"
"fmt"
"log"
"runtime"

_ "github.com/go-sql-driver/mysql"
)

type user struct {
id int
name string
age int
}

func main() {
log.SetFlags(log.Lshortfile)

// 连接数据库
db, err := sql.Open("mysql", "root:123456@tcp(192.168.1.13:3306)/test?charset=utf8")
if err != nil {
log.Panic(err)
}
defer db.Close()

// 测试连接
err = db.Ping()
handleErr(err)

// 增
fmt.Println("增----------------------------------------")
sqlStr := "insert into users(name, age) values (?, ?)"
res, err := db.Exec(sqlStr, "11", 11)
if err == nil {
id, e := res.LastInsertId()
handleErr(e)
fmt.Println("inserted, id:", id)

count, e := res.RowsAffected()
handleErr(e)
fmt.Println(count, "rows affected")
} else {
log.Println(err)
}

// 增,stmt
fmt.Println("增,stmt----------------------------------------")
stmt, err := db.Prepare(sqlStr)
res, err = stmt.Exec("12", 12)
if err == nil {
id, e := res.LastInsertId()
handleErr(e)
fmt.Println("inserted, id:", id)
} else {
log.Println(err)
}
stmt.Exec("13", 13)

// 改
fmt.Println("改----------------------------------------")
sqlStr = "update users set age = ? where name = ?"
db.Exec(sqlStr, 13, "12")

// 删
fmt.Println("删----------------------------------------")
sqlStr = "delete from users where name = ?"
db.Exec(sqlStr, "12")

// 查一条
fmt.Println("查----------------------------------------")
sqlStr = "select id, name, age from users where name = ?"
row := db.QueryRow(sqlStr, "11")
var u user
err = row.Scan(&u.id, &u.name, &u.age)
handleErr(err)
fmt.Printf("%#v\n", u)

// 查多条
fmt.Println("查----------------------------------------")
sqlStr = "select id, name, age from users"
rows, err := db.Query(sqlStr)
if err == nil {
for rows.Next() {
var u user
e := rows.Scan(&u.id, &u.name, &u.age)
handleErr(e)
fmt.Printf("%#v\n", u)
}
} else {
log.Println(err)
}
defer rows.Close()

// 事务
tx1, err := db.Begin()
if err == nil {
_, e := tx1.Exec("insert into users (name, age) value (?, ?)", "14", 14)
handleErr(e)
_, e = tx1.Exec("insert into users (name, age) value (?, ?)", "15", 15)
handleErr(e)
_, e = db.Exec("insert into users (name, age) value (?, ?)", "16", 16)
handleErr(e)
e = tx1.Rollback() // roolback之后会直接释放连接
handleErr(e)
} else {
log.Println(err)
}

tx2, err := db.Begin()
if err == nil {
_, e := tx2.Exec("insert into users (name, age) value (?, ?)", "17", 17)
handleErr(e)
_, e = tx2.Exec("insert into users (name, age) value (?, ?)", "18", 18)
handleErr(e)
e = tx2.Commit()
handleErr(e)
} else {
log.Println(err)
}
}

func handleErr(err error) {
if err != nil {
_, _, line, _ := runtime.Caller(1)
log.Println(line, err)
}
}

ORM

TODO go的ORM面临什么样的问题

TODO 目前有哪些还算不错的ORM,不同的ORM都是什么特点和风格
目前过有许多的ORM
知乎上看到的有

  1. gorm
  2. xorm
  3. gorose

gorm使用

xorm使用

参考

  1. 原理介绍
  2. 简单入门
  3. gorm官方教程