前言
通常情况下,我们程序员和数据库打交道是最多的。要然我们怎么会被称为码农呢。
存用户信息需要数据库,存订单需要数据库,等等等等,现在真是数据驱动着发展。
但是数据库种类有很多,有Mysql,Oracle,SQL Server。
本篇就示例如何Go操作Mysql。
准备工作
本次使用的是go mod进行包依赖管理,还不会使用的向上爬梯子,找go mod用法。
使用的库是第三方库go-sql-driver/mysql。
准备工作之连接数据库
代码
- func main() {
- var username = "root"
- var password = "rootroot"
- var ip = "127.0.0.1"
- var port = "3306"
- var data = "go_mysql_demo"
- var dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", username, password, ip, port, data)
- //Open只会验证dsb的格式是否正确,不会验证是否连接成功,同理,密码是否正确也不知道
- db, err := sql.Open("mysql", dsn)
- if err != nil {
- panic(err)
- }
- //关闭连接在 err 之后,因为可能直接就打开不成功,关闭一个没有打开的连接???
- defer db.Close()
- // 此时尝试连接数据库,会判断用户,密码,ip地址,端口是否正确
- err = db.Ping()
- if err != nil {
- fmt.Println("连接数据库失败,",err)
- return
- }
- //设置与数据库建立连接的最大数目,一般不管
- db.SetMaxOpenConns(100)
- //设置连接池中的最大闲置连接数,一般不管
- db.SetMaxIdleConns(50)
- }
注意
- sql.Open只会验证格式是否正确,不会连接数据库。
- db.Close在err之后,是因为可能打开不成功,关闭一个没有打开的连接。
- db.Ping会连接数据库,判断用户,密码,ip地址,端口是否正确。
准备工作之创建表
我们创建一个简单的用户表。
- CREATE TABLE `userinfo` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(10) DEFAULT NULL,
- `phone` char(11) DEFAULT NULL,
- `address` varchar(64) DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
准备工作之创建结构体
假设上述工作都完成了啊。
不知道有没有想过,我们查询的数据,存成啥?,字符串?map?切片?,似乎都不是太好。
只有结构体是最清晰的,最好认识的。
结构体
- type Userinfo struct {
- Id int64 `json:"id"`
- Name string `json:"name"`
- Phone string `json:"phone"`
- Address string `json:"address"`
- }
查询单条
单条查询使用QueryRow方法。
代码
- //查询单条
- sqlStr := "SELECT id,`name`,phone,address from userinfo where id = ?;"
- var user Userinfo
- /*
- QueryRow 第二个参数可以接收多个参数,同理,sqlStr可以有多个 ?占位符 进行匹配
- QueryRow 之后必须调用Scan方法进行数据绑定,进行数据库链接释放
- */
- err = db.QueryRow(sqlStr, 1).Scan(&user.Id, &user.Name, &user.Phone, &user.Address)
- if err != nil {
- fmt.Println("查询失败", err)
- return
- }
- fmt.Println(user)
执行结果
查询多条
多行查询使用Query。
代码
- //查询多条
- sqlStr := "SELECT id,`name`,phone,address from userinfo where id >= ?"
- //参数同 QueryRow
- rows, err := db.Query(sqlStr, 1)
- if err != nil {
- fmt.Println("查询失败:", err)
- return
- }
- // 此处使用rows释放所有链接
- defer rows.Close()
- //循环整理所有数据
- var userList = make([]Userinfo, 0, 10)
- for rows.Next() {
- var user Userinfo
- err = rows.Scan(&user.Id, &user.Name, &user.Phone, &user.Address)
- if err != nil {
- fmt.Println("绑定数据失败", err)
- return
- }
- userList = append(userList, user)
- }
- fmt.Println(userList)
执行结果
插入数据
插入数据需要用到Exec。
代码
- //插入数据
- sqlStr := "INSERT into userinfo(name,phone,address) values(?,?,?);"
- result, err := db.Exec(sqlStr, "吴彦祖", 555, "不知道哪的")
- if err != nil {
- fmt.Println("插入失败", err)
- return
- }
- //受影响的行数
- row_affect, err := result.RowsAffected()
- if err != nil {
- fmt.Println("受影响行数获取失败:", err)
- return
- }
- fmt.Println("受影响的行数:", row_affect)
- lastId, err := result.LastInsertId()
- if err != nil {
- fmt.Println("新增行id获取失败:", err)
- return
- }
- fmt.Println("新增行id:", lastId)
- fmt.Println("插入成功")
执行结果
Mysql
更新数据
更新和添加差不多,用的都是Exec。
代码
- //更新数据
- sqlStr := `UPDATE userinfo set name=? where id=?;`
- result, err := db.Exec(sqlStr, "吴彦祖666", 3)
- if err != nil {
- fmt.Println("更新失败", err)
- return
- }
- //受影响的行数
- row_affect, err := result.RowsAffected()
- if err != nil {
- fmt.Println("受影响行数获取失败:", err)
- return
- }
- fmt.Println("受影响的行数:", row_affect)
- fmt.Println("更新成功")
执行结果
Mysql
删除数据
删除数据用的还是Exec。
代码
- //删除数据
- sqlStr := "delete from userinfo where id = ?;"
- result, err := db.Exec(sqlStr, 3)
- if err != nil {
- fmt.Println("删除失败", err)
- return
- }
- //受影响的行数
- row_affect, err := result.RowsAffected()
- if err != nil {
- fmt.Println("受影响行数获取失败:", err)
- return
- }
- fmt.Println("受影响的行数:", row_affect)
- fmt.Println("删除成功")
执行结果
Mysql
事物
事物,这个用的就比较多了,通常用在关键的场景。
尤其是转账,张三-10块,李四+10块,这个动作动作是要在一起完成的。
如果任何一个失败了,就要恢复上一次的状态。
我们通常也叫这个操作叫做原子操作,要成功,都成功,要完蛋,都完蛋。
新建表
- CREATE TABLE `bill` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(8) NOT NULL,
- `money` int(11) NOT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
表数据
张三和李四都剩余100块
Go Mysql 关于事物相关方法
Go 关于事物有三个方法
- Begin()开始事物。
- Commit()提交事物。
- Rollback()失败回滚。
模拟转账:张三-10块,李四+十块
代码
- func main() {
- //事物
- //开启事物
- tx, err := db.Begin()
- if err != nil {
- //释放事物
- if tx != nil {
- tx.Rollback()
- }
- fmt.Println("事物开启失败")
- return
- }
- 张三减10块Sql := `UPDATE bill set money=money - 10 where name = ?;`
- result, err := tx.Exec(张三减10块Sql, "张三")
- if err != nil {
- //有错误表示更是失败,回滚原来状态
- tx.Rollback()
- fmt.Println(err)
- return
- }
- 张三受影响行数, err := result.RowsAffected()
- if err != nil {
- tx.Rollback() // 回滚
- return
- }
- 李四加10块Sql := `UPDATE bill set money=money + 10 where name = ?;`
- result, err = tx.Exec(李四加10块Sql, "李四")
- if err != nil {
- //有错误表示更是失败,回滚原来状态
- tx.Rollback()
- fmt.Println(err)
- return
- }
- 李四受影响行数, err := result.RowsAffected()
- if err != nil {
- tx.Rollback() // 回滚
- return
- }
- //都等于1表示成功,可以提交事务,修改数据
- if 张三受影响行数==1 && 李四受影响行数==1{
- //提交事务
- fmt.Println("提交事务")
- tx.Commit()
- }else{
- //有一个!=1表示没有更新成功,可能用户不存在
- fmt.Println("失败了,事物回滚了")
- tx.Rollback()
- }
- fmt.Println("事物执行成功")
- }
执行结果
Mysql
一加一减
假如出错了
Mysql
如果使用事物,出错了数据还是没变。
总结
本次主要讲述了Go如何操作Mysql,如何进行增删改查,最后还讲了以下什么是事物,如何操作事物。
当然,这种是最原始的方法,过程有些繁琐,了解入门就好,后面还有更方便的方法。