Go语言学习之操作MYSQL实现CRUD

作者:猿码记 时间:2024-01-21 15:33:14 

1.介绍

Go官方提供了database包,database包下有sql/driver。该包用来定义操作数据库的接口,这保证了无论使用哪种数据库,操作方式都是相同的。但Go官方并没有提供连接数据库的driver,如果要操作数据库,还需要第三方的driver包。

2.下载安装驱动

go-sql-driver驱动源码地址: https://github.com/go-sql-driver/mysql

2.1 安装驱动

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

3.匿名导入

通常来说,导入包后就能调用该包中的数据和方法。但是对于数据库操作来说,不应该直接使用导入驱动包所提供的方法,而应该使用 sql.DB对象所提供的统一的方法。因此在导入MySQL驱动时,使用了匿名导入包的方式。

匿名导入包: 只导入包但是不使用包内的类型和数据,使用匿名的方式: 在包路径前添加下画线_

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

在导入一个数据库驱动后,该驱动会自行初始化并注册到Godatabase/sql上下文中,这样就可以通过 database/sql 包所提供的方法来访问数据库了。

4.连接数据库

4.1 连接方法

使用sql包中的Open()函数来连接数据库。

Open(driverName, dataSourceName string) (*DB, error)
  • driverName: 使用的驱动名,如mysql。(注册到 database/sql时所使用的名字)

  • dataSourceName:数据库连接信息,格式:[用户名:密码@tcp(IP:port)/数据库?charset=utf8],例如:root:123456@tcp(127.0.0.1:3306)/test?charset=utf8

4.2 sql.DB作用

  • sql.Open()返回的sql.DB对象是Goroutine并发安全的。

  • sql.DB 通过数据库驱动为开发者提供管理底层数据库连接的打开和关闭操作。

  • sql.DB 帮助开发者管理数据库连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,<font color=red>如果开发者没有把连接释放回连接池,会导致过多连接使系统资源耗尽。</font>

4.3 sql.DB设计目标

sql.DB的设计目标就是作为长连接(一次连接多次数据交互)使用,不宜频繁开关。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象打开。如果需要短连接(一次连接一次数据交互),就把DB作为参数传入function,而不要在function中开关。

5.写操作(增、删、改)

5.1 执行步骤

  • 先使用预编译语句(PreparedStatement)来拼接sql

  • 然后调用db.Exec()执行SQL,返回执行结果

5.2 代码示例

package main

import (
   "database/sql"
   "fmt"
   _ "github.com/go-sql-driver/mysql"
   "time"
)
func main() {
   // 连接数据库
   open, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test?charset=utf8")
   checkError(err)
 //插入数据
   //add(open)
   // 更新数据
   //update(open)
   // 删除数据
   del(open)
}
//插入数据
func add(open *sql.DB)  {
   //插入数据
   prepare, err := open.Prepare("insert user set username=?,password=?,mobile=?,createtime=?")
   checkError(err)
   exec, err := prepare.Exec("李四", "123456", "17600000000", time.Now().Unix())
   checkError(err)
   id, err := exec.LastInsertId()
   checkError(err)
   fmt.Printf("插入数据ID: %d \n",id)
}
// 更新
func update(open *sql.DB)  {
   prepare, err := open.Prepare("update user set username=? where id =?")
   checkError(err)
   exec, err := prepare.Exec("王五", "18")
   checkError(err)
   rows, err := exec.RowsAffected()
   checkError(err)
   fmt.Printf("更新数据成功,影响条数 %d \n",rows)
}
// 删除数据
func del(open *sql.DB)  {
   prepare, err := open.Prepare("delete from user  where id =?")
   checkError(err)
   exec, err := prepare.Exec( "8")
   checkError(err)
   rows, err := exec.RowsAffected()
   checkError(err)
   fmt.Printf("删除数据成功,影响条数 %d \n",rows)
}
//检测错误
func checkError(err error)  {
   if err != nil {
       panic("操作失败:"+err.Error())
   }
}

6. 读操作(查询)

6.1 执行步骤

1. 查询多条步骤

  • 调用db.Query()方法执行SQL语句,返回一个Rows查询结果。

  • rows.Next()方法的返回值作为for循环的条件,迭代查询数据。

  • 在循环中,通过 rows.Scan()方法读取每一行数据。

  • 调用db.Close()关闭查询。

2.查询单条步骤

  • 调用db.QueryRow()方法执行SQL语句,返回一个Row查询结果。

  • 然后调用row.Scan()读取数据。

6.2 代码示例

package main
import (
   "database/sql"
   "fmt"
   _ "github.com/go-sql-driver/mysql"
)
func main() {
   // 连接数据库
   db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/nsbd_app?charset=utf8")
   checkError(err)
   //查询多条数据
   rows := queryRows(db)
   fmt.Printf("多条返回: \n%+v\n",rows)
   // 查询单条数据
   row := queryRow(db)
   fmt.Printf("单条返回: \n%+v\n",row)
}

// 创建表的映射对象
type User struct {
   Uid        int
   UserName   string
   CreateTime int
   Birthday   sql.NullString //有的值可能为NULL
}

//查询多条数据
func queryRows(db *sql.DB) []User {
   stmt, err := db.Prepare("select id,username,createtime,birthday from nsbd_user where id < ?")
   checkError(err)
   rows, err := stmt.Query(30)
   // 延迟关闭
   defer rows.Close()
   checkError(err)
   user := new(User)
   //users := make([]User,5)
   var users []User
   for rows.Next() {
       // rows.Scan()方法的参数顺序很重要,必须和查询结果的column相对应(数量和顺序都需要一致)
       err := rows.Scan(&user.Uid, &user.UserName, &user.CreateTime, &user.Birthday)
       checkError(err)
       users = append(users, *user)
   }
   return users
}
// 查询单条数据
func queryRow(db *sql.DB) User {
   stmt, err := db.Prepare("select id,username,createtime,birthday from nsbd_user where id = ?")
   checkError(err)
   user := new(User)
   err = stmt.QueryRow(4).Scan(&user.Uid, &user.UserName, &user.CreateTime, &user.Birthday)
   checkError(err)
   return *user
}
//检测错误
func checkError(err error) {
   if err != nil {
       panic("操作失败:" + err.Error())
   }
}

输出:

多条返回: 
[{Uid:1 UserName:admin CreateTime:0 Birthday:{String:2017-04-15 Valid:true}} {Uid:2 UserName:u2 CreateTime:1605858072 Birthday:{String:1993-02-14 Valid:true}} {Uid:3 UserName:u3 CreateTime:1606289644 Birthday:{String:1991-05-31 Valid:true}} {Uid:4 UserName:u4 CreateTime:1610521164 Birthday:{String:1989-11-24 Valid:true}} {Uid:5 UserName:u5 CreateTime:1610588359 Birthday:{String: Valid:false}}]
单条返回: 
{Uid:4 UserName:u4 CreateTime:1610521164 Birthday:{String:1989-11-24 Valid:true}}

6.3 注意事项

  • rows.Scan()方法的参数顺序很重要,必须和查询结果的column相对应(数量和顺序都需要一致);

  • Go是强类型语言,在查询数据时先定义数据类型,针对字段值为NULL时,数据类型应定义为:sql.NullString、sql.NullInt64等,并可以通过Valid值来判断查询到的值是赋值状态还是未赋值状态。

  • 每次db.Query()操作后,都建议调用rows.Close()rows.Close()操作是幂等操作,即便对已关闭的rows再执行close()也没关系。

6.4 为什么查询后要关闭连接

因为db.Query()会从数据库连接池中获取一个连接,这个底层连接在结果集(rows)未关闭前会被标记为处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows.Close()。但如果出现异常,提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭,则此连接会一直被占用。因此通常使用defer rows.Close()来确保数据库连接可以正确放回到连接池中。

来源:https://segmentfault.com/a/1190000043421582

标签:Go,MYSQL,CRUD
0
投稿

猜你喜欢

  • 彻底解决页面文字编码乱码问题

    2022-09-17 02:08:49
  • 使用PyInstaller将Pygame库编写的小游戏程序打包为exe文件及出现问题解决方法

    2021-09-17 08:53:15
  • 如何使用索引提高查询速度

    2024-01-15 02:14:15
  • Python实现二维曲线拟合的方法

    2021-08-26 12:38:01
  • 详解Visual Studio中Git的简单使用

    2022-05-25 13:22:04
  • anaconda python3.8安装后降级

    2021-09-12 05:28:51
  • python管理包路径之pycharm自动解决包路径注册

    2023-07-14 14:25:38
  • Python使用Numpy模块读取文件并绘制图片

    2022-03-11 07:11:58
  • Django配合python进行requests请求的问题及解决方法

    2023-11-02 02:30:47
  • JavaScript 如何在线解压 ZIP 文件

    2024-04-22 22:14:58
  • python+OpenCV实现图像拼接

    2023-01-28 08:09:26
  • Python文件读写w+和r+区别解析

    2022-01-12 04:23:04
  • 使用Python在Windows下获取USB PID&VID的方法

    2021-02-15 19:31:20
  • "模板化"——限制还是激发

    2009-03-26 11:36:00
  • 使用pycharm生成代码模板的实例

    2021-03-07 04:57:52
  • Python2.x版本中maketrans()方法的使用介绍

    2021-02-14 21:04:25
  • ASP.NET连接SQL数据库的简单实例代码

    2024-01-26 06:54:27
  • php.ini修改php上传文件大小限制的方法详解

    2023-09-11 04:03:12
  • vue3配置全局参数(挂载全局方法)以及组件的使用

    2023-07-02 16:45:46
  • Go REFLECT Library反射类型详解

    2024-04-26 17:25:15
  • asp之家 网络编程 m.aspxhome.com