Go语言操作MySQL

Go MySQL 教程展示了如何在 Golang 中使用 MySQL。这些示例执行基本的数据库操作。

$ go version
go version go1.18.1 linux/amd64

我们使用 Go 版本 1.18。

MySQL 是领先的开源数据库管理系统。它是一个多用户、多线程的数据库管理系统。MySQL 在网络上特别流行。MariaDB 是 MySQL 关系数据库管理系统的社区开发、商业支持的分支。

Go 有一个sql包,它提供了一个围绕 SQL(或类似 SQL)数据库的通用接口。sql 包必须与数据库驱动程序一起使用。

该软件包提供自动连接池。每次查询数据库时,我们都在使用应用程序启动时设置的连接池中的连接。连接被重用。

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

我们需要安装 MySQL 驱动程序。

city_mysql.sql

USE testdb;
DROP TABLE IF EXISTS cities;
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), population INT);
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);

在本教程中,我们使用该cities表。

Go MySQL 版本

在第一个示例中,我们打印 MySQL 的版本。

version.go

package main

import (
    "database/sql"
    "fmt"
    "log"

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

func main() {

    db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")
    defer db.Close()

    if err != nil {
        log.Fatal(err)
    }

    var version string

    err2 := db.QueryRow("SELECT VERSION()").Scan(&version)

    if err2 != nil {
        log.Fatal(err2)
    }

    fmt.Println(version)
}

程序返回 MySQL 的版本。版本由执行SELECT VERSION()语句确定。

_“github.com/go-sql-driver/mysql”

当导入带有空白标识符前缀的包时,将调用包的 init 函数。该函数注册驱动程序。

db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")

使用sql.Open,我们打开一个由其数据库驱动程序名称和驱动程序特定数据源名称指定的数据库,通常至少由数据库名称和连接信息组成。它不与数据库建立任何连接,也不验证驱动程序连接参数。相反,它只是为以后使用准备数据库抽象。当第一次需要时,将延迟建立与底层数据存储的第一个实际连接。

defer db.Close()

Close连接返回到连接池。db.Close如果 sql.DB 的生命周期不应超出函数范围,则 延迟是惯用的。

err2 := db.QueryRow("SELECT VERSION()").Scan(&version)

QueryRow执行一个预计最多返回一行的查询 。该Scan函数将匹配行中的列复制到version变量中。

$ go run version.go
10.3.24-MariaDB-2

Go MySQL 使用 Query 选择所有行

执行返回行的Query查询,通常是 SELECT。可选参数用于查询中的任何占位符参数。select_all.go

package main

import (
    "database/sql"
    "fmt"
    "log"

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

type City struct {
    Id         int
    Name       string
    Population int
}

func main() {

    db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")
    defer db.Close()

    if err != nil {
        log.Fatal(err)
    }

    res, err := db.Query("SELECT * FROM cities")

    defer res.Close()

    if err != nil {
        log.Fatal(err)
    }

    for res.Next() {

        var city City
        err := res.Scan(&city.Id, &city.Name, &city.Population)

        if err != nil {
            log.Fatal(err)
        }

        fmt.Printf("%v\n", city)
    }
}

该示例打印cities表中的所有行。

for res.Next() {

Next准备下一个结果行以使用该 方法Scan读取。成功时返回 true,如果没有下一个结果行或在准备时发生错误,则返回 false。

err := res.Scan(&city.Id, &city.Name, &city.Population)

我们将数据读入City结构Scan中。

$ go run select_all.go
{1 Bratislava 432000}
{2 Budapest 1759000}
{3 Prague 1280000}
{4 Warsaw 1748000}
{5 Los Angeles 3971000}
{6 New York 8550000}
{7 Edinburgh 464000}
{8 Berlin 3671000}

使用 Exec 执行 MySQL 插入行

Exec函数执行查询而不返回任何行。它与 INSERT、UPDATE、DELETE 或 DROP 语句一起使用。可选参数用于查询中的任何占位符参数。insert_row.go

package main

import (
    "database/sql"
    "fmt"
    "log"

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

func main() {

    db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")
    defer db.Close()

    if err != nil {
        log.Fatal(err)
    }

    sql := "INSERT INTO cities(name, population) VALUES ('Moscow', 12506000)"
    res, err := db.Exec(sql)

    if err != nil {
        panic(err.Error())
    }

    lastId, err := res.LastInsertId()

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("The last inserted row id: %d\n", lastId)
}

该示例在表中插入一个新行cities

sql := "INSERT INTO cities(name, population) VALUES ('Moscow', 12506000)"
res, err := db.Exec(sql)

我们在表中插入一个新城市。

lastId, err := res.LastInsertId()

有了LastInsertId,我们得到最后插入的 id。

Go MySQL 预处理语句

对于预处理语句,我们使用占位符而不是直接将值写入语句。预处理的语句提高了数据库操作的安全性和性能。

Go 在幕后为您创建准备好的语句。

db.Query(sql, param1, param2)

Query函数准备 SQL,然后使用参数执行它,最后关闭语句。

prepared.go

package main

import (
    "database/sql"
    "fmt"
    "log"

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

type City struct {
    Id         int
    Name       string
    Population int
}

func main() {

    db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")
    defer db.Close()

    if err != nil {
        log.Fatal(err)
    }

    var myid int = 1

    res, err := db.Query("SELECT * FROM cities WHERE id = ?", myid)
    defer res.Close()

    if err != nil {
        log.Fatal(err)
    }

    if res.Next() {

        var city City
        err := res.Scan(&city.Id, &city.Name, &city.Population)

        if err != nil {
            log.Fatal(err)
        }

        fmt.Printf("%v\n", city)
    } else {

        fmt.Println("No city found")
    }
}

该示例从数据库中选择一行。

res, err := db.Query("SELECT * FROM city WHERE id = ?", myid)

是一个占位符,?填充了 myid变量中的值。在幕后,db.Query 实际上准备、执行和关闭准备好的语句。

$ go run prepared.go
{1 Bratislava 432000}

Go MySQL 受影响的行

RowsAffected 返回受更新、插入或删除语句影响的行数 。

affected_rows.go

package main

import (
    "database/sql"
    "fmt"
    "log"

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

func main() {

    db, err := sql.Open("mysql", "user7:s$cret@tcp(127.0.0.1:3306)/testdb")
    defer db.Close()

    if err != nil {
        log.Fatal(err)
    }

    sql := "DELETE FROM cities WHERE id IN (2, 4, 6)"
    res, err := db.Exec(sql)

    if err != nil {
        panic(err.Error())
    }

    affectedRows, err := res.RowsAffected()

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("The statement affected %d rows\n", affectedRows)
}

在代码示例中,我们使用 DELETE SQL 语句删除三行。然后我们用 打印删除的行数RowsAffected

$ go run affected_rows.go 
The statement affected 3 rows

在本教程中,我们在 Go 中使用了 MySQL。