GO学习之 数据库(mysql)

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/sqlgithub.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/sqlgithub.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 有哪些优势呢?

  1. 高性能:Go 语言本身的设计目标就是高性能,所以也能够获得高性能的表现。
  2. 并发支持:Go 语言天生支持并发,可以轻松的实现并发的数据库操作,更加适合高并发的操作。
  3. 直接访问数据库:Go 语言通过标准库database/sql对SQL数据库直接访问的接口,允许直接操作数据库。

有哪些缺点呢?

  1. 生态系统相对较新:相对于其他语言,Go 语言在社区和工具等相对较新,不是很全面。
  2. ORM 支持有限:Go 语言的 ORM(对象关系映射)相对与其他语言成熟度较低,需要手动编写SQL更多。

第三方开源库用于操作 MySQL 数据库:

  1. database/sql:Go 语言标准库提供了database/sql包,支持多种数据库的操作。它提供了通用的接口的方法,让你能够进行基本的数据库操作。
  2. github.com/go-sql-driver/mysql:这是一个 MySQL 驱动,对 MySQL 数据库进行操作。
  3. github.com/jinzhu/gorm:GORM 是 Go 语言中一个流行的 ORM 框架,它 提供了对数据库的高级抽象,支持多种数据库。它可以简化数据库的操作,但是性能上有些损耗。
  4. github.com/go-xorm/xorm:XORM 是一个流行的 ORM 框架,它提供了对多种数据库的支持。它有一些独特的特性,适用于一些特定的场景。
  5. github.com/jmoiron/sqlx: SQLx 是 database/sql 的扩展,提供更方便的数据库操作方法。

现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值