![cover](https://img-blog.csdnimg.cn/img_convert/16da6262399c40c0a4a3f873eb05d63e.png)
GO学习之 数据库(mysql)
对数据库操作,少不了各个语言对数据库操作的驱动,就像 JAVA 中有 mysql-driver 的驱动包,拉取下来就可以通过JDBC 对数据库操作了,当然 Spring、Mybatis 等框架也提供了对数据库很方便的操作。那在 Go 中也是提供了驱动 ``github.com/go-sql-driver/mysql``,我们通过 go get 拉取驱动来进行CRUD操作。
GO系列
1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
12、GO学习之 数据库(mysql)
13、GO学习之 数据库(Redis)
14、GO学习之 搜索引擎(ElasticSearch)
15、GO学习之 消息队列(Kafka)
16、GO学习之 远程过程调用(RPC)
17、GO学习之 goroutine的调度原理
18、GO学习之 通道(nil Channel妙用)
19、GO学习之 同步操作sync包
20、GO学习之 互斥锁、读写锁该如何取舍
21、GO学习之 条件变量 sync.Cond
22、GO学习之 单例模式 sync.Once
23、GO 面试题总结一【面试官这样问】
24、GO 面试题进阶篇【面试官这样问】
前言
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
数据持久化是必不可少的一部分,平日里开发,如果是专注于业务开发,那 99% 的工作也就是CRUD(增删改查)工程师了。
废话不多说,说了也没用,直接上手来操作,对数据库进行访问。
一、简介
对数据库操作,少不了各个语言对数据库操作的驱动,就像 JAVA 中有 mysql-driver 的驱动包,拉取下来就可以通过JDBC 对数据库操作了,当然 Spring、Mybatis 等框架也提供了对数据库很方便的操作。
那在 Go 中也是提供了驱动 github.com/go-sql-driver/mysql
,我们通过 go get 拉取驱动来进行CRUD操作。
使用命令:go get github.com/go-sql-driver/mysql
来拉取驱动库。
二、准备操作
首先在 MYSQL 数据库创建一个测试库叫 go_demo
,然后来添加一张 User
表来操作,脚本如下:
- 新建表:
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(0) NULL DEFAULT NULL,
`create_time` date NOT NULL,
`update_time` date NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
- 插入测试数据
INSERT INTO `users` (name, address, age, create_time, update_time) VALUES ('悟空', '五指山下', 18, NOW(), NOW());
INSERT INTO `users` (name, address, age, create_time, update_time) VALUES ('唐僧', '大唐东土', 21, NOW(), NOW());
INSERT INTO `users` (name, address, age, create_time, update_time) VALUES ('八戒', '高老庄', 19, NOW(), NOW());
INSERT INTO `users` (name, address, age, create_time, update_time) VALUES ('沙森', '流沙河', 25, NOW(), NOW());
- 查询数据
select * from users;
三、Insert 操作
下面的示例中,首先使用
database/sql
和github.com/go-sql-driver/mysql
包来连接 MySQL 数据库,获取到一个连接示例 db, 再通过db.Exec()
函数来执行insert
语句,成功把 白龙马 指派到了师徒四人的队伍中。
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 连接 MYSQL 数据库
db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/go_demo")
if err != nil {
log.Fatal(err)
}
// defer 关键字来延迟关闭连接
defer db.Close()
// 插入数据
insertSql := "INSERT INTO users (name, address, age, create_time, update_time) VALUES (?, ?, ?, ?, ?)"
datetime := time.Now()
_, insertErr := db.Exec(insertSql, "白龙马", "东海", 18, datetime, datetime)
if insertErr != nil {
log.Fatal(insertErr)
}
fmt.Println("插入数据成功!")
}
四、Delete 操作
下面的示例中,首先使用
database/sql
和github.com/go-sql-driver/mysql
包来连接 MySQL 数据库,获取到一个连接示例 db, 再通过db.Exec()
函数来执行delete
语句,成功把 悟空 逐出了队伍。
可以看出来,从获取数据库连接到执行 SQL 语句,Go 代码基本一样的,想必定有框架做了此事了……
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 连接mysql数据库
db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/go_demo")
if err != nil {
log.Fatal(err)
}
defer db.Close()
//删除悟空
deleteSql := "delete from users where id = ?"
_, deleteErr := db.Exec(deleteSql, 1)
if deleteErr != nil {
log.Fatal(deleteErr)
}
fmt.Println("删除成功!")
}
五、Update 操作
更新操作其实无外乎也是获取数据库连接,执行
update
语句,我们可以先封装一个公共的函数来获取数据库连接,在执行操作。
5.1 获取数据库链接
这里的包是
common
, 在另一个包中,并且 Conn 首字母大写,表明外部包是可以调用的。
package common
import (
"database/sql"
"log"
_ "github.com/go-sql-driver/mysql"
)
func Conn() *sql.DB {
// 连接mysql数据库
db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/go_demo")
if err != nil {
log.Fatal(err)
}
return db
}
5.2 更新操作
下面案例,从上面封装的 common 包中通过 Conn() 函数获取数据库连接,再进行操作。
**注意:**这里需要用common.Conn()
获取连接,不能用*common.Conn()
获取,如果用*common.Conn()
获取则在 后续的判断中db != nil
出错:mismatched types sql.DB and untyped nil
,因为db
是一个指针类型,不能直接与nil
进行比较。
package main
import (
"fmt"
"log"
"gotest.com/test/src/common"
)
func main() {
// 获取数据库连接
db := common.Conn()
if db != nil {
defer db.Close()
// 更新八戒的地址,不再是高老庄
updateSql := "update users set address = ? where id = ?"
_, updateErr := db.Exec(updateSql, "天上人间", 3)
if updateErr != nil {
log.Fatal(updateErr)
}
fmt.Println("更新成功!")
} else {
fmt.Println("获取数据库连接失败!")
}
}
六、Select 操作
在下面的案例中:
- 通过 自己封装的
common.Conn()
获取一个数据库链接- 利用
defer
关键词来延迟关闭链接- 通过
db.Query()
来执行一个查询- 循环遍历,
rows.Next()
来判断是否有下一条记录,如果返回 true,大括号{}
表示一个代码块,其中包含了每次迭代式要执行的代码。- 在
{}
中,可以对每条记录进行操作,比如利用Scan()
来扫描将记录的值赋值给相应定义的变量。- 主要注意的是,rows.Scan(&id, &name…) 是指向 id、name 变量的指针,才能把记录中的值赋值给当前的变量。
package main
import (
"fmt"
"log"
"gotest.com/test/src/common"
)
func main() {
// 获取数据库连接
db := common.Conn()
selectSql := "select id,name,address,age from users"
rows, err := db.Query(selectSql)
if err != nil {
log.Fatal(err)
}
// 延迟关闭数据库连接
defer db.Close()
// 遍历结果, rows是数据库查询结果的迭代器,Next()函数来判断是否下一条记录
for rows.Next() {
var id int
var name string
var address string
var age int
err := rows.Scan(&id, &name, &address, &age)
if err != nil {
log.Fatal(err)
}
fmt.Println("id:", id, "name:", name, "address:", address, "age:", age)
}
}
五、事务
下面示例中,使用
db.Begin()
来开启事务,本来要更新唐僧的地址为女儿国,唐僧也对女儿国王动心了,本来和女儿国王都说好了,玉帝哥哥陪国王白头到老(没头发如何白头到老),但是贫僧有要事在身,不得不离开,所以即便更新成功了,也得通过tx.Rollback()
回滚回去。
package main
import (
"fmt"
"log"
"gotest.com/test/src/common"
)
func main() {
// 获取数据库连接
db := common.Conn()
if db != nil {
defer db.Close()
// 开启事务
tx, beginErr := db.Begin()
if beginErr != nil {
log.Fatal(beginErr)
}
// 更新唐僧的地址,不再是东土大唐,留在了女儿国
// 注意,这里要用事务 tx.Exec() 来执行操作
updateSql := "update users set address = ? where id = ?"
result, updateErr := db.Exec(updateSql, "女儿国", 2)
count, _ := result.RowsAffected()
// 如果影响行数大于 0
if count > 0 {
// 唐僧有使命在身,怎能为了儿女私情误了朕的大事
tx.Rollback()
fmt.Println("唐僧最终离开了女儿国!")
return
}
if updateErr != nil {
log.Fatal(updateErr)
// 更新操作发生异常,事务回滚
tx.Rollback()
}
// 提交事务
commitErr := tx.Commit()
if commitErr != nil {
log.Fatal(commitErr)
fmt.Println("更新提交失败!")
}
fmt.Println("更新成功!")
} else {
fmt.Println("获取数据库连接失败!")
}
}
- 运行前数据库记录如下:
- 运行结果如下:
可以看到,更新条数是 1,说明更新成功了!!!
PS D:\workspaceGo\src\database> go run .\updateTest.go
更新条数: 1
唐僧最终离开了女儿国!
2023/08/19 21:29:26 sql: transaction has already been committed or rolled back
exit status 1
- 运行后数据库记录如下:
注意
:在使用 tx, beginErr := db.Begin()
开启事务后,需要用 tx.Exec()
来执行更新操作,这样后续的tx.Rollback()
和tx.Commit()
操作才会生效。刚开始我就使用了db.Exec()
来执行的更新操作,结果回滚就不生效,才发现犯了这等低级错误…
六、总结
此篇只对 Go 语言操作数据库进行简单的 CRUD 操作的示例,就像 JAVA 中用 JDBC 查库那样的基础操作,可发现基本就是通过获取的数据库链接通过函数进行对SQL的执行操作。
那 Go 操作 MySQL 有哪些优势呢?
- 高性能:Go 语言本身的设计目标就是高性能,所以也能够获得高性能的表现。
- 并发支持:Go 语言天生支持并发,可以轻松的实现并发的数据库操作,更加适合高并发的操作。
- 直接访问数据库:Go 语言通过标准库
database/sql
对SQL数据库直接访问的接口,允许直接操作数据库。
有哪些缺点呢?
- 生态系统相对较新:相对于其他语言,Go 语言在社区和工具等相对较新,不是很全面。
- ORM 支持有限:Go 语言的 ORM(对象关系映射)相对与其他语言成熟度较低,需要手动编写SQL更多。
第三方开源库用于操作 MySQL 数据库:
- database/sql:Go 语言标准库提供了
database/sql
包,支持多种数据库的操作。它提供了通用的接口的方法,让你能够进行基本的数据库操作。 - github.com/go-sql-driver/mysql:这是一个 MySQL 驱动,对 MySQL 数据库进行操作。
- github.com/jinzhu/gorm:GORM 是 Go 语言中一个流行的 ORM 框架,它 提供了对数据库的高级抽象,支持多种数据库。它可以简化数据库的操作,但是性能上有些损耗。
- github.com/go-xorm/xorm:XORM 是一个流行的 ORM 框架,它提供了对多种数据库的支持。它有一些独特的特性,适用于一些特定的场景。
- github.com/jmoiron/sqlx: SQLx 是 database/sql 的扩展,提供更方便的数据库操作方法。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!
更多推荐
所有评论(0)