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)

文章目录

  • GO系列
  • 前言
  • 一、简介
  • 二、准备操作
  • 三、Insert 操作
  • 四、Delete 操作
  • 五、Update 操作
    • 5.1 获取数据库链接
    • 5.2 更新操作
  • 六、Select 操作
  • 五、事务
  • 六、总结

前言

按照公司目前的任务,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("获取数据库连接失败!")
	}
}

  • 运行前数据库记录如下:
    GO学习之 数据库(mysql)_第1张图片
  • 运行结果如下:

可以看到,更新条数是 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 的扩展,提供更方便的数据库操作方法。

你可能感兴趣的:(golang学习,SQL数据库,数据库,golang,mysql)