Go操作MySql数据库的方式

Go操作Mysql数据库

使用Go操作MySQL等数据库,一般有两种方式:一是使用database/sql接口,直接在代码里硬编码sql语句;二是使用gorm,即对象关系映射的方式在代码里抽象的操作数据库。一般推荐使用第二种方式。

使用database/sql接口

Go没有内置的驱动支持任何数据库,但是Go定义了database/sql接口,用户可以基于驱动接口开发相应数据库的驱动。但缺点是,直接用 github.com/go-sql-driver/mysql 访问数据库都是直接写 sql,取出结果然后自己拼成对象,使用上面不是很方便,可读性也不好。

下载包

1
go get github.com/go-sql-driver/mysql

安装好 mysql 驱动之后,我们创建一张user表,并执行CRUD操作(增、删、改、查)。代码如下所示:

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"time"
)

//数据库连接信息
const (
USERNAME = "root"
PASSWORD = "123456"
NETWORK = "tcp"
SERVER = "127.0.0.1"
PORT = 3306
DATABASE = "test"
)

//user表结构体定义
type User struct {
Id int `json:"id" form:"id"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
Status int `json:"status" form:"status"` // 0 正常状态, 1删除
Createtime int64 `json:"createtime" form:"createtime"`
}

func main() {
conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)
DB, err := sql.Open("mysql", conn)
if err != nil {
fmt.Println("connection to mysql failed:", err)
return
}

DB.SetConnMaxLifetime(100*time.Second) //最大连接周期,超时的连接就close
DB.SetMaxOpenConns(100) //设置最大连接数
CreateTable(DB)
InsertData(DB)
QueryOne(DB)
QueryMulti(DB)
UpdateData(DB)
DeleteData(DB)
}

func CreateTable(DB *sql.DB) {
sql := `CREATE TABLE IF NOT EXISTS users(
id INT(4) PRIMARY KEY AUTO_INCREMENT NOT NULL,
username VARCHAR(64),
password VARCHAR(64),
status INT(4),
createtime INT(10)
); `

if _, err := DB.Exec(sql); err != nil {
fmt.Println("create table failed:", err)
return
}
fmt.Println("create table successd")
}

//插入数据
func InsertData(DB *sql.DB) {
result,err := DB.Exec("insert INTO users(username,password) values(?,?)","test","123456")
if err != nil{
fmt.Printf("Insert data failed,err:%v", err)
return
}
lastInsertID,err := result.LastInsertId() //获取插入数据的自增ID
if err != nil {
fmt.Printf("Get insert id failed,err:%v", err)
return
}
fmt.Println("Insert data id:", lastInsertID)

rowsaffected,err := result.RowsAffected() //通过RowsAffected获取受影响的行数
if err != nil {
fmt.Printf("Get RowsAffected failed,err:%v",err)
return
}
fmt.Println("Affected rows:", rowsaffected)
}

//查询单行
func QueryOne(DB *sql.DB) {
user := new(User) //用new()函数初始化一个结构体对象
row := DB.QueryRow("select id,username,password from users where id=?", 1)
//row.scan中的字段必须是按照数据库存入字段的顺序,否则报错
if err := row.Scan(&user.Id,&user.Username,&user.Password); err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Println("Single row data:", *user)
}

//查询多行
func QueryMulti(DB *sql.DB) {
user := new(User)
rows, err := DB.Query("select id,username,password from users where id = ?", 2)

defer func() {
if rows != nil {
rows.Close() //关闭掉未scan的sql连接
}
}()
if err != nil {
fmt.Printf("Query failed,err:%v\n", err)
return
}
for rows.Next() {
err = rows.Scan(&user.Id, &user.Username, &user.Password) //不scan会导致连接不释放
if err != nil {
fmt.Printf("Scan failed,err:%v\n", err)
return
}
fmt.Println("scan successd:", *user)
}
}

//更新数据
func UpdateData(DB *sql.DB){
result,err := DB.Exec("UPDATE users set password=? where id=?","111111",1)
if err != nil{
fmt.Printf("Insert failed,err:%v\n", err)
return
}
fmt.Println("update data successd:", result)

rowsaffected,err := result.RowsAffected()
if err != nil {
fmt.Printf("Get RowsAffected failed,err:%v\n",err)
return
}
fmt.Println("Affected rows:", rowsaffected)
}

//删除数据
func DeleteData(DB *sql.DB){
result,err := DB.Exec("delete from users where id=?",1)
if err != nil{
fmt.Printf("Insert failed,err:%v\n",err)
return
}
fmt.Println("delete data successd:", result)

rowsaffected,err := result.RowsAffected()
if err != nil {
fmt.Printf("Get RowsAffected failed,err:%v\n",err)
return
}
fmt.Println("Affected rows:", rowsaffected)
}

执行程序,输出结果如下所示:

1
2
3
4
5
6
7
8
create table successd
Insert data id: 1
Affected rows: 1
Single row data: {1 test 123456 0 0}
update data successd: {0xc0000a0000 0xc000010280}
Affected rows: 1
delete data successd: {0xc0000a0000 0xc0000102b0}
Affected rows: 1

OK,到这里大家是不是觉得这种实现方式很繁琐,假如要修改某个sql语句需要在代码中修改,这样很麻烦,代码设计也比较糟糕。因此这种方式并不推荐使用。

使用GORM

GORM(Object Relation Mapping),即Go语言中的对象关系映射,实际上就是对数据库的操作进行封装,对上层开发人员屏蔽数据操作的细节,开发人员看到的就是一个个对象,大大简化了开发工作,提高了生产效率。如GORM结合Gin等服务端框架使用可以开发出丰富的Rest API等。

首先,下载包

1
2
go get github.com/jinzhu/gorm
go get github.com/gin-gonic/gin

使用Go的Gin框架和Gorm开发简单的CRUD API,代码如下

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

import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"net/http"
)

var MysqlDB *gorm.DB

type User struct {
Id int `gorm:"size:11;primary_key;AUTO_INCREMENT;not null" json:"id"`
Age int `gorm:"size:11;DEFAULT NULL" json:"age"`
Name string `gorm:"size:255;DEFAULT NULL" json:"name"`
//gorm后添加约束,json后为对应mysql里的字段
}

func main() {
MysqlDB, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8")
if err != nil {
fmt.Println("failed to connect database:", err)
return
}else{
fmt.Println("connect database success")
MysqlDB.SingularTable(true)
MysqlDB.AutoMigrate(&User{}) //自动建表
fmt.Println("create table success")
}
defer MysqlDB.Close()

Router()
}

func Router() {
router := gin.Default()
//路径映射
router.GET("/user", InitPage)
router.POST("/user/create", CreateUser)
router.GET("/user/list", ListUser)
router.PUT("/user/update", UpdateUser)
router.GET("/user/find", GetUser)
router.DELETE("/user/:id", DeleteUser)
router.Run(":8080")
}

//每个路由都对应一个具体的函数操作,从而实现了对user的增,删,改,查操作
func InitPage(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}

func CreateUser(c *gin.Context) {
var user User
c.BindJSON(&user) //使用bindJSON填充对象
MysqlDB.Create(&user) //创建对象
c.JSON(http.StatusOK, &user) //返回页面
}

func UpdateUser(c *gin.Context) {
var user User
id := c.PostForm("id") //post方法取相应字段
err := MysqlDB.First(&user, id).Error //数据库查找主键=ID的第一行
if err != nil {
c.AbortWithStatus(404)
fmt.Println(err.Error())
} else {
c.BindJSON(&user)
MysqlDB.Save(&user) //提交更改
c.JSON(http.StatusOK, &user)
}
}
func ListUser(c *gin.Context) {
var user []User
line := c.Query("line")
MysqlDB.Limit(line).Find(&user) //限制查找前line行
c.JSON(http.StatusOK, &user)
}
func GetUser(c *gin.Context) {
id := c.Query("id")
var user User
err := MysqlDB.First(&user, id).Error
if err != nil {
c.AbortWithStatus(404)
fmt.Println(err.Error())
} else {
c.JSON(http.StatusOK, &user)
}
}

func DeleteUser(c *gin.Context) {
id := c.Param("id")
var user User
MysqlDB.Where("id = ?", id).Delete(&user)
c.JSON(http.StatusOK, gin.H{
"data": "this has been deleted!",
})
}

执行程序,输出结果如下所示:

1
2
3
4
5
6
7
8
9
10
connect database success
create table success
......
[GIN-debug] GET /user --> main.InitPage (3 handlers)
[GIN-debug] POST /user/create --> main.CreateUser (3 handlers)
[GIN-debug] GET /user/list --> main.ListUser (3 handlers)
[GIN-debug] PUT /user/update --> main.UpdateUser (3 handlers)
[GIN-debug] GET /user/find --> main.GetUser (3 handlers)
[GIN-debug] DELETE /user/:id --> main.DeleteUser (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080

如果要对上述接口进行测试,可以使用postman等工具.经测试可以实现对user的增,删,改,查操作。

如下,使用GET方法请求/user接口。
image

小结

通过非常简短的代码,就可以实现功能强大的restful接口,go语言的优势也是非常明显的.关于gin和gorm还有更加深入的内容.掌握好这两个工具可以轻松的构建web应用。

0%