首页 > 数据库 >Go 操作 MySQL 数据库

Go 操作 MySQL 数据库

时间:2022-11-25 12:08:46浏览次数:32  
标签:err 数据库 sql db Println MySQL Go fmt


这一期讲一讲如何使用 Go 操作 MySQL 数据库,这里就不讲 MySQL 的安装以及配置了,但要记得开启 MySQL 服务,我这里使用的是 MySQL 8.0.20 版本。


Go 操作 MySQL 数据库_mysql

加载数据库驱动

Go 操作 MySQL 数据库_mysql_02


想要连接到数据库,首先需要加载目标数据库的驱动,驱动里面包含着与该数据库交互的逻辑。在 Go 中我们使用 sql 包下的 ​​Open()​​ 方法设置连接数据库的参数:

func Open(driverName, dataSourceName string) (*DB, error)

第一个参数是数据库驱动的名称,第二个参数是数据源名称,该函数会返回一个指向 ​​sql.DB​​​ 的指针,结构体 ​​sql.DB​​ 如下:

type DB struct {
waitDuration int64

connector driver.Connector
numClosed uint64

mu sync.Mutex
freeConn []*driverConn
connRequests map[uint64]chan connRequest
nextRequest uint64
numOpen int

openerCh chan struct{}
closed bool
dep map[finalCloser]depSet
lastPut map[*driverConn]string
maxIdleCount int
maxOpen int
maxLifetime time.Duration
maxIdleTime time.Duration
cleanerCh chan struct{}
waitCount int64
maxIdleClosed int64
maxIdleTimeClosed int64
maxLifetimeClosed int64

stop func()
}

​sql.DB​​ 是用来操作数据库的,它代表了 0 个或者多个底层连接池,这些连接都由 sql 包维护,该包会自动地创建和释放这些连接,它是线程安全的。

​Open()​​ 函数并不会连接数据库,甚至不会验证其参数。它只是把后续连接到数据库所必需的 structs 给设置好了,而真正的连接是在被需要的时候才进行懒设置的。

​sql.DB​​​ 不需要进行关闭(当然你想关闭也是可以的),它就是用来处理数据库的,而不是实际的连接。这个抽象包含了数据库连接的池,而且会对此进行维护。在使用 ​​sql.DB​​ 的时候,可以定义它的全局变量进行使用,也可以将它传递函数/方法里。

正常获得驱动的做法是使用 ​​sql.Register()​​ 函数:

func Register(name string, driver driver.Driver)

该函数需传入数据库驱动的名称和一个实现了 ​​driver.Driver​​ 接口的结构体,来注册数据库的驱动。


Go 操作 MySQL 数据库_mysql

第三方驱动自动注册

Go 操作 MySQL 数据库_mysql_02


当第三方数据库包被引入到时候,它的 ​​init​​ 函数将会运行并进行自我注册。

并且在引入包的时候,把该包的名设置为下划线 ​​_​​ ,这是因为我们不直接使用数据库驱动,我们只使用  database/sql ,如果未来升级驱动,也无需改变代码。

Go 没有提供官方的数据库驱动,所有的数据库驱动都是第三方驱动,但是它们都遵循 ​​sql.driver​​ 包里面定义的接口,我们需要安装第三方函数库。当然,安装第三方库之前,因为你所知道的某些原因,可能会出现下载安装失败的问题,所以先要配置代理。打开命令行,在项目目录下执行下面两条命令:

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.io,direct

使用下面的命令初始化项目:

go mod init go-db(项目名称)

然后我们再使用 ​​go get​​ 命令安装第三方库。

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

接着,我们先在 MySQL 中创建一个名为 godb_test 的数据库:

CREATE DATABASE godb_test;

进入该数据库:

use godb_test;

执行以下命令创建一张用于测试的用户数据表:

CREATE TABLE `acl_user` (
`id` bigint unsigned NOT NULL COMMENT '会员ID',
`phone` char(11) NOT NULL COMMENT '手机号码',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`age` int DEFAULT '0' COMMENT '年龄',
`is_deleted` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除1(true)已删除,0(false)未删除',
PRIMARY KEY (`id`)
) COMMENT='用户表';

Go 操作 MySQL 数据库_mysql

测试数据库连接

Go 操作 MySQL 数据库_mysql_02


接着我们用 Go 编写连接数据库代码,测试数据库能否连接成功:

package main

import (
"database/sql"
"fmt"

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

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()
}

上面的代码中,连接参数可以有以下几种格式:

user@unix(/path/to/socket)/dbname?charset=utf8
user:password@tcp(localhost:5555)/dbname?charset=utf8
user:password@/dbname
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

通常我们使用第二种。运行该程序,输出如下:

Database connection succeeded

证明测试数据库连接成功。


Go 操作 MySQL 数据库_mysql

插入操作

Go 操作 MySQL 数据库_mysql_02


插入操作我们使用的是 ​​Exec()​​ 方法:

func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
return db.ExecContext(context.Background(), query, args...)
}

func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
return tx.ExecContext(context.Background(), query, args...)
}

func (s *Stmt) Exec(args ...interface{}) (Result, error) {
return s.ExecContext(context.Background(), args...)
}

​Exec()​​​ 执行一次命令(包括查询、删除、更新、插入等),返回的 ​​Result​​​ 是对已执行的 SQL 命令的总结。参数 ​​args​​ 表示 query 中的占位参数。下面是插入数据的例子:

package main

import (
"database/sql"
"fmt"

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

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

// 准备 SQL 语句
stmt, err := db.Prepare(`insert into acl_user(id, phone, nick_name, age) values (?,?,?,?)`)
if err != nil {
fmt.Println("Prepare fail")
return
}
fmt.Println("Prepare succeeded")

// 将参数传递到 SQL 语句中并执行
_, err = stmt.Exec("101", "13112345678", "caizi", 20)
if err != nil {
fmt.Println("Exec fail")
return
}
fmt.Println("Exec succeeded")

}

运行该程序后,输出如下证明已经成功地插入了数据:

Database connection succeeded
Prepare succeeded
Exec succeeded

Go 操作 MySQL 数据库_mysql

修改操作

Go 操作 MySQL 数据库_mysql_02


修改操作与上面的插入操作基本一致,下面是修改数据的例子:

package main

import (
"database/sql"
"fmt"

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

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

// 准备 SQL 语句
stmt, err := db.Prepare(`update acl_user set phone = ?, age = ? where id = ? and is_deleted = 0`)
if err != nil {
fmt.Println("Prepare fail")
return
}
fmt.Println("Prepare succeeded")

// 将参数传递到 SQL 语句中并执行
_, err = stmt.Exec("13122222223", 18, "101")
if err != nil {
fmt.Println("Exec fail")
return
}
fmt.Println("Update succeeded")
}

运行该程序后,输出如下证明已经成功地更新了数据:

Database connection succeeded
Prepare succeeded
Update succeeded

Go 操作 MySQL 数据库_mysql

查询操作

Go 操作 MySQL 数据库_mysql_02


查询操作分为单行查询和多行查询,单行查询使用的是 ​​QueryRow()​​ 方法:

func (db *DB) QueryRow(query string, args ...interface{}) *Row {
return db.QueryRowContext(context.Background(), query, args...)
}

​QueryRow()​​​ 执行一次查询,并期望返回最多一行结果。​​QueryRow()​​​ 总是返回非 ​​nil​​​ 的值,直到返回值的 ​​Scan()​​​ 方法被调用时,才会返回被延迟的错误。​​Scan()​​ 将查询结果赋值到对应的变量中。下面是单行查询的例子:

package main

import (
"database/sql"
"fmt"

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

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

var nickName string
// QueryRow 后调用 Scan 方法,否则持有的数据库链接不会被释放
err = db.QueryRow(`select nick_name from acl_user where id = ?`, 101).Scan(&nickName)
if err != nil {
fmt.Println("Query fail")
return
}
fmt.Println("nickName: ", nickName)
}

运行上面的程序,我们得到的结果如下:

Database connection succeeded
nickName: caizi

多行查询使用的是 ​​Query()​​ 方法:

func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
return db.QueryContext(context.Background(), query, args...)
}

下面是多行查询的例子,在查询前我先向数据表添加了一些数据:

package main

import (
"database/sql"
"fmt"

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

type User struct {
id int
phone string
nickName string
age int
}

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

rows, err := db.Query(`select id, nick_name, phone, age from acl_user where id > 0 and is_deleted = 0`)
if err != nil {
fmt.Println("Query fail")
return
}

var users []User
for rows.Next() {
var user User
err := rows.Scan(&user.id, &user.nickName, &user.phone, &user.age)
if err != nil {
fmt.Println("Rows fail")
}
users = append(users, user)
}

for _, v := range users {
fmt.Println(v)
}
}

运行该程序输出如下:

Database connection succeeded
{101 13122222223 caizi 18}
{102 13612345678 John 25}
{103 13712345689 Mary 20}

Go 操作 MySQL 数据库_mysql

删除操作

Go 操作 MySQL 数据库_mysql_02


删除操作使用的还是 ​​Exec()​​ 方法,下面是删除数据的例子:

package main

import (
"database/sql"
"fmt"

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

type User struct {
id int
phone string
nickName string
age int
}

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

fmt.Println("Database connection succeeded")

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

// 准备 SQL 语句
stmt, err := db.Prepare(`delete from acl_user where id = ?`)
if err != nil {
fmt.Println("Prepare fail")
return
}
fmt.Println("Prepare succeeded")

// 将参数传递到 SQL 语句中并执行
_, err = stmt.Exec("101")
if err != nil {
fmt.Println("Exec fail")
return
}
fmt.Println("Exec succeeded")
}

运行该程序输出如下,可以看到已经删除成功:

Database connection succeeded
Prepare succeeded
Exec succeeded




标签:err,数据库,sql,db,Println,MySQL,Go,fmt
From: https://blog.51cto.com/u_15891283/5886188

相关文章