Go语言中操作MySQL

幸运草
幸运草
幸运草
896
文章
3
评论
2020年4月16日20:51:54 评论 112
1 概述2 MySQL 驱动安装2 打开数据库3 数据源名称4 查询操作查询多行查询单行NULL处理5 非查询操作6 预编译7 事务

1 概述

Go 语言程序可以作为 MySQL 服务器的客户端完成 MySQL 数据库的操作。

Go 语言中,使用 database/sql 包实现类 SQL 数据库的操作。sql 包是一个数据库抽象层,具体的数据库操作的实现要依赖于相应的驱动才可以。抽象层与驱动的关系,如下图所示:

Go语言中操作MySQL
数据库抽象层

Go 支持的驱动列表请参考:https://github.com/golang/go/wiki/SQLDrivers,或者 SQL驱动列表

2 MySQL 驱动安装

MySQL 的驱动由 github.com/go-sql-driver/mysql 实现,使用前保证该驱动正确安装。下面的命令会将 mysql 驱动包安装到环境变量 GOPATH 命令中:

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

2 打开数据库

函数 func sql.Open(driverName, dataSourceName string) (*DB, error) 用于打开一个 dirverName 指定的数据库。打开数据库时,需要指定 dataSourceName 数据源名称,数据源名称中包含所需要连接的数据库服务器信息。一个典型的打开 mysql 驱动的数据库语法为:

db, err := sql.Open("mysql", "root:hellokang@tcp(localhost:3306)/test")

驱动的名称就是: mysql,数据源名称就是:root:hellokang@tcp(localhost:3306)/test。

打开数据库后,会获得 sql.DB 类型对象,该对象引用了一个数据库连接池,可以安全的被多个 Go 进程同时使用。同时会维护自身的闲置连接池,因此,通常情况 Open 函数只需调用一次,并很少需要关闭。若需要显式关闭使用函数 func (db *DB) Close() error 完成。

打开数据库并不是建立连接。指的是 Open 函数可能只是验证其参数,而不创建与数据库的连接。可以调用 func (db *DB) Ping() error 方法来检查数据源的名称的合法性。

3 数据源名称

数据源名称:DSN,data Source Name

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]

其中除了 /dbname 数据库名称外,其余的部分都是可选的。若不需要选择默认数据库,则使用 / 即可(也可留空)。

  • username,用户名
  • password,密码
  • protocol,协议,支持tcp, Unix domain socket
  • address,地址。tcp 协议下,使用 host[:port],端口省略表示默认3306。在 Unix domain sockets 协议下,使用 /var/run/mysqld/mysqld.sock 指定 MySQL-Server 的 socket 文件。
  • /dbname,默认数据库名,使用 / 表示不指定默认数据库。

常用参数为:

  • charset,C-S 交互时的字符集(SET NAMES charset)。此参数会额外执行一条 SQL,因此更建议使用 collation 选项。
  • collation,C-S 交互时的校对集。
  • columnsWithAlias,布尔值,默认为 false。设置为 true 表示获取列名时,会包含表别名部分。

全部参数请参考:http://go.hellokang.net/mysql-dsn.html

4 查询操作

查询操作,通常指的是 select、show 类操作。

查询多行

函数 func (db *DB) Query(query string, args ...interface{}) (*Rows, error) 执行查询,返回多行,sql.Rows 类对象,参数 query 为查询 SQL。args 为 query 中占位参数。

得到的 sql.Rows 对象为结果集,需要使用其 func (rs *Rows) Next() bool 和 func (rs *Rows) Scan(dest ...interface{}) error 方法完成遍历和获取结果集中的每行数据。结果集使用完毕后应该及时关闭,方法 func (rs *Rows) Close() error 关闭结果集。

示例代码为:

// 执行查询
rows, err := db.Query("SELECT ...")
// 关闭结果集
defer rows.Close()
// 遍历结果集
for rows.Next() {
    var (
      id int
      name string
    )
    // 获取行内字段
    err = rows.Scan(&id, &name)
}
// 获取遍历时发生的错误
err = rows.Err() 
...

函数 func (rs *Rows) Err() error 返回在遍历时出现的错误。需要在 Close 后调用。

查询单行

函数 func (db *DB) QueryRow(query string, args ...interface{}) *Row 执行查询,返回一行,sql,Row 类对象,参数 query 为查询 SQL。args 为 query 中占位参数。

得到的 sql.Row 对象为单行结果集,需要使用其 func (r *Row) Scan(dest ...interface{}) error 方法完成获取结果集中字段数据。如果该查询匹配多行,Scan 会使用第一行结果并丢弃其余各行,如果没有匹配查询的行,Scan 会返回 ErrNoRows。

示例代码:

var username string
err := db.QueryRow("SELECT ... WHERE id=?", 42).Scan(&username)
if sql.ErrNoRows == err {
  log.Println("No result with that ID")
}

NULL处理

若字段允许为 NULL,那么意味着获取的可能为 NULL。应该使用 sql.NullBool、sql.NullInt64、sql.NullFloat64、NullString 类型来读取查询到字段值。这些类型的结构类似,都是:

type NullString struct {
    String string
    Valid  bool // 如果 String 不是 NULL 则 Valid 为真
}

示例如下:

var s sql.NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
if s.Valid {
  // 非 NULL
  // use s.String 
} else {
  // NULL
  // NULL value
}

5 非查询操作

非查询操作,指的是不返回结果记录的 SQL 操作,例如 INSERT、DELETE、UPDATE、以及 DDL 等。

函数 func (db *DB) Exec(query string, args ...interface{}) (Result, error) 完成执行 SQL 但不返回结果集,函数的返回值为 sql.Result 类型对象,用于获取影响行数或最新生成的ID。参数 query 为查询 SQL。args 为 query 中占位参数。

示例为:

result, err := db.Exec("INSERT INTO tableName (name) VALUES (?)", "Hank")
if nil != err {
  log.Println(err)
}
id, _ := result.LastInsertId()
fmt.Println("最新的 ID为:", id)
  • result.LastInsertId() (int64, error) 方法获取最新的ID
  • result.RowsAffected() (int64, error) 方法获取影响的记录行数

6 预编译

Go 支持预编译的方式执行 SQL。

函数 func (db *DB) Prepare(query string) (*Stmt, error) 用于预编译 SQL,会返回 sql.Stmt 对象于之后的执行。预编译时,不需要设置数据,使用问号占位即可。

预编译完成后,使用以下函数执行:

  • func (s *Stmt) Exec(args …interface{}) (Result, error),执行非查询类语句
  • func (s Stmt) Query(args …interface{}) (Rows, error),执行查询类语句,返回多行的 Rows 对象
  • func (s *Stmt) QueryRow(args …interface{}) *Row,执行查询类语句,返回单行的 Row 对象

执行时,需要传递参数,为预编译时的数据占位。执行完毕后,对结果集的操作与普通执行是一致的,遍历,读取等。

得到的 sql.Stmt 对象可以绑定不同数据反复执行,一次编译,多次执行。

sql.Stmt 对象 使用完毕,需要关闭。函数 func (s *Stmt) Close() error 用来关闭。

示例如下:

stmt, err := db.Prepare("SELECT name FROM users WHERE id = ?")
if err != nil {
    log.Fatal(err)
}
id := 42
rows, err := stmt.Query(id)
// 后续获取数据代码不再赘述

7 事务

Go 语言支持 MySQL 事务。以下函数用于操作事务:

  • func (db *DB) Begin() (*Tx, error),开启事务,返回 Tx 对象引用
  • func (tx *Tx) Commit() error,提交事务
  • func (tx *Tx) Rollback() error,回滚事务
  • func (tx *Tx) Exec(query string, args …interface{}) (Result, error),执行非查询类SQL
  • func (tx *Tx) Query(query string, args …interface{}) (*Rows, error),执行查询类SQL,返回多条
  • func (tx *Tx) QueryRow(query string, args …interface{}) *Row,执行查询类sQL,返回单条
  • func (tx *Tx) Prepare(query string) (*tmt, error),预编译
  • func (tx *Tx) Stmt(stmt *Stmt) *Stmt,将事务外预编译好的语句对象纳入事务

示例为:

tx,_ := db.Begin()
tx.Exec("INSERT INTO user(id, username, age) values(?, ?)", "Hank", 42)
tx.Exec("UPDATE userCounter set counter=couter+1)")
tx.Commit()

完!

特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

转载请注明:{{title}}-变化吧
  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 赞助本站
  • 支付宝扫一扫
  • weinxin
幸运草
Go语言接口规则 前端框架

Go语言接口规则

Go语言接口规则 接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口对应的全部方法签名。就表示它 "实现" 了该接口,无须在该类型上显式声明实现了哪个接口。对应方法,是指有相同名称、参数...
Go语言中处理 HTTP 服务器 前端框架

Go语言中处理 HTTP 服务器

1 概述 包 net/http 提供了HTTP服务器端和客户端的实现。本文说明关于服务器端的部分。 快速开始: package main import (   "log"   "net/http" )...