Go连接Mysql

1.database/sql标准包

sql包提供了保证SQL或类SQL数据库的泛用接口。使用sql包时必须注入(至少)一个数据库驱动。参见http://golang.org/s/sqldrivers 获取驱动列表。如使用mysql,则需要注入github.com/go-sql-driver/mysql。

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
package main

import (
"database/sql"
"fmt"
"os"
"strconv"

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

// Person pojo
type Person struct {
UserID int `db:"user_id"`
UserName string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
}

func (p *Person) String() string {
return strconv.Itoa(p.UserID) + p.UserName + p.Sex + p.Email
}

// ErrHandle 错误处理
func ErrHandle(err error, where string) {
if err != nil {
fmt.Println("[ERR]:", err, where)
os.Exit(0)
}
}

func main() {
db, err := sql.Open("mysql", "root:toor@tcp(127.0.0.1:3306)/test?charset=utf8")
ErrHandle(err, "connection mysql faild")
defer func() {
err = db.Close()
ErrHandle(err, "db.close()")
}()

// 新增操作
// 准备sql语句
insertSQL := "insert into person (`username`,`sex`,`email`) VALUES (?,?,?)"
// 预执行语句,stmt的主要方法:Exec、Query、QueryRow、Close返回*Stmt声明句柄,stmt的主要方法:Exec、Query、QueryRow、Close
stmt, err := db.Prepare(insertSQL)
ErrHandle(err, "dn.prepar(insertSQL)")
// 执行
result, err := stmt.Exec("database/sql", "man", "[email protected]")
ErrHandle(err, "stmt.Exec(insertSQL)")
// 获取插入结果的id
id, err := result.LastInsertId()
ErrHandle(err, "LastInsertId()")
fmt.Println("LastInsertId:", id)

// 修改操作
updataSQL := "UPDATE PERSON SET sex = ? WHERE user_id = ?"
// 预执行语句
stmt, err = db.Prepare(updataSQL)
ErrHandle(err, "db.prepar(updataSQL)")
// 执行 修改上面新增的数据
result, err = stmt.Exec("woman", id)
ErrHandle(err, "stmt.Exec(updataSQL)")
id, err = result.RowsAffected()
ErrHandle(err, "RowsAffected()")
fmt.Println("RowsAffected:", id)

// 查询操作
// 1. 查询一条记录 必须定义一个用来接收查询结果的变量
resultPerson := new(Person)
// 准备SQL语句
querySQL := "SELECT person.username, person.sex, person.email FROM person WHERE user_id =?"
//查询一条,返回一条结果。并赋值到resultPerson这个结构体类型的变量中,就算查询到的是多条,返回的还是一条
err = db.QueryRow(querySQL, id).Scan(&resultPerson.UserName, &resultPerson.Sex, &resultPerson.Email)
ErrHandle(err, "db.QueryRow(querySQL)")
fmt.Printf("[query row]:%v\n", resultPerson)

// 2. 查询多条记录
querySQL = "SELECT * FROM person"
// 执行查询,返回多行
queryRows, err := db.Query(querySQL)
ErrHandle(err, "db.Query")
defer func() {
err = queryRows.Close()
ErrHandle(err, "queryRows.Close()")
}()
// 遍历结果集 *Rows.NEXT() bool
for queryRows.Next() {
err = queryRows.Scan(&resultPerson.UserID, &resultPerson.UserName, &resultPerson.Sex, &resultPerson.Email)
ErrHandle(err, "queryRows.Scan(&resultPerson)")
fmt.Printf("[query rows]:%v\n\n", resultPerson)
}

// 删除操作
delSQL := "delete from person where user_id = ?"
stmt, err = db.Prepare(delSQL)
ErrHandle(err, "db.Prepare(delSQL)")
// 执行 删除上面新增的数据
result, err = stmt.Exec(id)
ErrHandle(err, "stmt.Exec(delSQL)")
id, err = result.RowsAffected()
ErrHandle(err, "RowsAffected()")
fmt.Println("RowsAffected:", id)

// 事务操作
// 开启事务
tx, err := db.Begin()
ErrHandle(err, "db.Begin()")
// 开始事务操作
_, err1 := tx.Exec("UPDATE PERSON SET sex = ? WHERE user_id = ?", "woman", id)
_, err2 := tx.Exec("DELETE FROM person WHERE user_id = ?", id)
if err1 != nil || err2 != nil {
fmt.Println("事务操作出错,开始回滚")
// 事务回滚
tx.Rollback()
} else {
// 事务提交
tx.Commit()
}
}

2.jmoiron/sqlx”第三方包

sqlx为对database/sql的封装,提高对sql支持的易用性。其使用方式和database/sql相差不大。

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
package main

import (
"fmt"
"os"

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

// Person pojo
type Person struct {
UserID int `db:"user_id"`
UserName string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
}

func (p *Person) String() string {
return string(p.UserID) + p.UserName + p.Sex + p.Email
}

// Db mysql连接
var Db *sqlx.DB

// 初始化
func init() {
// 初始化连接
database, err := sqlx.Open("mysql", "root:toor@tcp(127.0.0.1:3306)/test")
if err != nil {
fmt.Println("connection mysql faild:", err)
return
}
Db = database
}

// ErrHandle 通用错误处理
func ErrHandle(err error, where string) {
if err != nil {
fmt.Println("[Err]:", err, where)
os.Exit(0)
}
}

// 新增
func insert() {
result, err := Db.Exec("insert into person (`username`,`sex`,`email`) VALUES (?,?,?)", "stu03", "man", "[email protected]")
ErrHandle(err, "mysql insert")
id, err := result.LastInsertId()
ErrHandle(err, "result.LastInsertId()")
fmt.Println("LastInsertId:", id)
}

// 修改
func updata() {
result, err := Db.Exec("UPDATE PERSON SET sex = ? WHERE user_id = ?", "woman", 1)
ErrHandle(err, "mysql updata")
id, err := result.RowsAffected()
ErrHandle(err, "result.RowsAffected()")
fmt.Println("RowsAffected:", id)
}

// 删除
func del() {
result, err := Db.Exec("delete from person where user_id = ?", 6)
ErrHandle(err, "mysql delete")
id, err := result.RowsAffected()
ErrHandle(err, "result.RowsAffected()")
fmt.Println("RowsAffected:", id)
}

// 查询
func query() {
// 定义一个slice接收查询结果
var ps []Person
err := Db.Select(&ps, "SELECT person.username, person.sex, person.email FROM person WHERE user_id =?", 1)
ErrHandle(err, "mysql select")
fmt.Println(ps)
}

func main() {
insert()
updata()
del()
query()
}

sqlx对事务的操作非常简单:

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
package main

import (
"fmt"
"os"

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

// Person pojo
type Person struct {
UserID int `db:"user_id"`
UserName string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
}

func (p *Person) String() string {
return string(p.UserID) + p.UserName + p.Sex + p.Email
}

// Db mysql连接连接池
var Db *sqlx.DB

// 初始化
func init() {
// 初始化连接
database, err := sqlx.Open("mysql", "root:toor@tcp(127.0.0.1:3306)/test")
if err != nil {
fmt.Println("connection mysql faild:", err)
return
}

Db = database
}

// ErrHandle 错误处理
func ErrHandle(err error, where string) {
if err != nil {
fmt.Println("[ERR]:", err, where)
os.Exit(0)
}
}

// 事务管理
func mysqlTransaction() {
// 开启事务
tx, err := Db.Begin()
ErrHandle(err, "db.begin")

// 开始事务操作
_, err1 := tx.Exec("UPDATE PERSON SET sex = ? WHERE user_id = ?", "woman", 1)
_, err2 := tx.Exec("DELETE FROM person WHERE user_id = ?", 1)
if err1 != nil || err2 != nil {
fmt.Println("事务操作出错,开始回滚")
// 事务回滚
tx.Rollback()
} else {
// 事务提交
tx.Commit()
}
}

func main() {
mysqlTransaction()
}

sqlx自带连接池管理,在项目中可直接用连接池管理对mysql的访问,以实现对IO的访问控制:

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
package main

import (
"fmt"
"os"
"time"

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

var db *sqlx.DB

func init() {
database, err := sqlx.Open("mysql", "root:toor@tcp(127.0.0.1:3306)/test")
if err != nil {
fmt.Println("connection mysql faild")
}

//设置最大打开的连接数,设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。默认值为0表示不限制。
database.SetMaxOpenConns(512)
// 设置最大空闲连接数
database.SetMaxIdleConns(64)
// 设置超时时间
database.SetConnMaxLifetime(time.Second * 60)

db = database
}

// ErrHandle 错误处理
func ErrHandle(err error, where string) {
if err != nil {
fmt.Println("[ERR]:", err, where)
os.Exit(0)
}
}

func insert() {
result, err := db.Exec("insert into person (`username`,`sex`,`email`) VALUES (?,?,?)", "stu03", "man", "[email protected]")
ErrHandle(err, "mysql insert")
id, err := result.LastInsertId()
ErrHandle(err, "result.LastInsertId()")
fmt.Println("LastInsertId:", id)
}

func main() {
insert()
}

拓展:

3.gorm第三方包

ORM(Object Relation Mapping)中文翻译为对象关系映射,这是一种把数据库记录映射为对象类型的一种技术,它把对数据的的操作变成对对象的操作,而不用在意其内部的sql语句,这对许多基于OOP开发的程序员无疑是福音。go非常热门的orm框架 github.com/jinzhu/gorm ,已经实现了全功能的orm,使用方式请直接参考开发文档,该文档已提供中文支持:https://gorm.io/zh_CN/docs/index.html。 下面我们来简单使用一下其基本功能。

安装库:go get -u github.com/jinzhu/gorm

导入库及设置连接池:

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
//导入库与mysql驱动
import(
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/jinzhu/gorm"
)

//定义一个通用的错误处理函数
func ErrorHandler(err error, where string) {
if err != nil {
fmt.Println("出现错误:", err, where)
os.Exit(1)
}
}


//连接数据库
var db *gorm.DB
//包初始化时连接
func init() {
var err error
db, err = gorm.Open("mysql", "<username>:<password>/<database>?charset=utf8&parseTime=True&loc=Local")
ErrorHandler(err,"gorm.Open()")

//database/sql 已内置连接池设置
db.DB().SetMaxIdleConns(20)
db.DB().SetMaxOpenConns(100)
}

模型定义:

1
2
3
4
5
6
7
8
9
10
//通过标签设置 mysql 里面的约束
type User struct {
gorm.Model //嵌入gorm基础模型,可获得模型基础能力
Name string `gorm:"type:varchar(12);not null;unique_index"`
Email string `gorm:"type:varchar(24);unique_index"`
Phone string `gorm:"type:varchar(11);unique_index"`
Avatar string `gorm:"type:varchar(64)"`
Title string `gorm:"type:varchar(20)"`
Gender int32 `gorm:"type:varchar(2)"`
}

表操作:

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
//创建表
if !db.HasTable(&User{}) {
err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8").CreateTable(&User{}).Error
ErrorHandler(err,"CreateTable()")
}


//1.插入操作
user := &User{
Name: "fun",
Email:"[email protected]",
Phone:"13813812138",
Avatar:"link/to/your/avatar.jpg",
Title:"Gopher",
Gender:1
CreatedAt: time.Now(),
}

if err := db.Create(user).Error; err != nil {
log.Println("Create data fail!")
}

//2.更新操作
// UPDATE users SET name='joker', updated_at='2016-04-20 22:12:52' WHERE id=1;
db.Model(&user).Update("name", "joker")
// UPDATE users SET name='joker',title='Coder', updated_at='2016-04-20 22:12:52' WHERE id=1;
db.Model(&user).Updates(User{Name: "joker", Title: "Coder"})

//3.查询操作
//通过主键查询第一条记录
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
// 随机取一条记录
db.Take(&user)
//// SELECT * FROM users LIMIT 1;
// 通过主键查询最后一条记录
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// 拿到所有的记录
db.Find(&users)
//// SELECT * FROM users;
// 查询指定的某条记录(只可在主键为整数型时使用)
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;

//条件查询
// 获取单条记录
db.Where("name = ?", "fun").First(&user)
// SELECT * FROM users WHERE name = 'fun' limit 1;
// 获取多条记录
db.Where("name = ?", "fun").Find(&users)
//// SELECT * FROM users WHERE name = 'fun';
// <>
db.Where("name <> ?", "fun").Find(&users)
// IN
db.Where("name IN (?)", []string{"fun", "joker"}).Find(&users)
// LIKE
db.Where("name LIKE ?", "%f%").Find(&users)
// AND
db.Where("name = ? AND age >= ?", "fun", "18").Find(&users)
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)


//4.删除操作
// 删除一条
db.Delete(&user)
// DELETE from users where id=1;
//条件删除多条
db.Where("name LIKE ?", "%fun%").Delete(User{})
// DELETE from users where name LIKE "%f%";
//条件删除多条
db.Delete(User{}, "name LIKE ?", "%fun%")
// DELETE from users where name LIKE "%fun%";