Go Web 示例: MySQL 数据库

简介

某个时间点,你希望你的 Web 应用程序存储和检索数据库中的数据。 当你处理动态内容、为用户提供表单以输入数据或存储登录名和密码凭据以供用户对其进行身份验证时,几乎总是如此。 为此,我们有数据库。

数据库有各种形式和形式。 整个网络中一种常用的数据库是 MySQL 数据库。 它已经存在了很长时间,并且已经证明了它的位置和稳定性的次数比你想象的要多。

在这个例子中,我们将深入研究 Go 中数据库访问的基础知识,创建数据库表,存储数据并再次检索它。

安装 go-sql-driver/mysql 包

Go 编程语言带有一个名为“database/sql”的方便的包,用于查询各种 SQL 数据库。 这很有用,因为它将所有常见的 SQL 功能抽象到一个 API 中供你使用。 Go 不包括数据库驱动程序。 在 Go 中,数据库驱动程序是一个包,它实现了特定数据库(在我们的例子中是 MySQL)的底层细节。 你可能已经猜到了,这对于保持向前兼容很有用。 因为,在创建所有 Go 包时,作者无法预见未来每个数据库都会上线,并且支持每个可能的数据库将有大量的维护工作。

要安装 MySQL 数据库驱动程序,请转到你选择的终端并运行:

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

连接到 MySQL 数据库

安装所有必需的软件包后,我们需要检查的第一件事是,我们是否可以成功连接到我们的 MySQL 数据库。 如果你还没有运行 MySQL 数据库服务器,你可以轻松地使用 Docker 启动一个新实例。 以下是 Docker MySQL 镜像的官方文档:https://hub.docker.com/_/mysql

要检查我们是否可以连接到我们的数据库,请导入 database/sql 和 go-sql-driver/mysql 包并打开一个连接,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import "database/sql"
import _ "go-sql-driver/mysql"


// Configure the database connection (always check errors)
db, err := sql.Open("mysql", "username:password@(127.0.0.1:3306)/dbname?parseTime=true")



// Initialize the first connection to the database, to see if everything works correctly.
// Make sure to check the error.
err := db.Ping()

创建我们的第一个数据库表

我们数据库中的每个数据条目都存储在一个特定的表中。 数据库表由列和行组成。 这些列为每个数据条目提供了一个标签并指定了它的类型。 行是插入的数据值。 在我们的第一个例子中,我们想创建一个这样的表:

id username password Created_at
1 johndoe secret 2019-08-10 12:30:00

转换为 SQL,创建表的命令如下所示:

1
2
3
4
5
6
7
CREATE TABLE users (
    id INT AUTO_INCREMENT,
    username TEXT NOT NULL,
    password TEXT NOT NULL,
    created_at DATETIME,
    PRIMARY KEY (id)
);

现在我们有了 SQL 命令,我们可以使用 database/sql 包在 MySQL 数据库中创建表:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
query := `
    CREATE TABLE users (
        id INT AUTO_INCREMENT,
        username TEXT NOT NULL,
        password TEXT NOT NULL,
        created_at DATETIME,
        PRIMARY KEY (id)
    );`

// Executes the SQL query in our database. Check err to ensure there was no error.
_, err := db.Exec(query)

插入我们的第一个用户

如果你熟悉 SQL,则向我们的表中插入新数据就像创建我们的表一样简单。 需要注意的一件事是:默认情况下,Go 使用准备好的语句将动态数据插入到我们的 SQL 查询中,这是一种将用户提供的数据安全地传递到我们的数据库而没有任何损坏风险的方法。 在 Web 编程的早期,程序员直接将数据与查询一起传递到数据库,这会导致大量漏洞并可能破坏整个 Web 应用程序。 请不要这样做。 很容易做到正确。

为了将我们的第一个用户插入到我们的数据库表中,我们创建了一个如下的 SQL 查询。 如你所见,我们省略了 id 列,因为它由 MySQL 自动设置。 问号告诉 SQL 驱动程序,它们是实际数据的占位符。 在这里你可以看到我们谈到的准备好的语句。

1
INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)

我们现在可以在 Go 中使用这个 SQL 查询并在我们的表中插入一个新行:

1
2
3
4
5
6
7
8
9
import "time"

username := "johndoe"
password := "secret"
createdAt := time.Now()

// Inserts our data into the users table and returns with the result and a possible error.
// The result contains information about the last inserted id (which was auto-generated for us) and the count of rows this query affected.
result, err := db.Exec(`INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)`, username, password, createdAt)

要为你的用户获取新创建的 ID,只需像这样获取它:

1
userID, err := result.LastInsertId()

查询我们的用户表

现在我们的表中有一个用户,我们想查询它并取回它的所有信息。 在 Go 中,我们有两种可能性来查询我们的表。 有 db.Query 可以查询多行,供我们迭代,还有 db.QueryRow 以防我们只想查询特定行。

查询特定行的工作方式基本上与我们之前介绍的所有其他 SQL 命令一样。 我们通过 ID 查询单个用户的 SQL 命令如下所示:

1
SELECT id, username, password, created_at FROM users WHERE id = ?

在 Go 中,我们首先声明一些变量来存储我们的数据,然后像这样查询单个数据库行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var (
    id        int
    username  string
    password  string
    createdAt time.Time
)

// Query the database and scan the values into out variables. Don't forget to check for errors.
query := `SELECT id, username, password, created_at FROM users WHERE id = ?`
err := db.QueryRow(query, 1).Scan(&id, &username, &password, &createdAt)

查询所有用户

在之前的部分中,我们介绍了如何查询单个用户行。 许多应用程序都有你想要查询所有现有用户的用例。 这与上面的示例类似,但涉及更多的编码。

我们可以使用上面示例中的 SQL 命令并删除 WHERE 子句。 这样,我们查询所有现有用户。

1
SELECT id, username, password, created_at FROM users

在 Go 中,我们首先声明一些变量来存储我们的数据,然后像这样查询单个数据库行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
type user struct {
    id        int
    username  string
    password  string
    createdAt time.Time
}

rows, err := db.Query(`SELECT id, username, password, created_at FROM users`) // check err
defer rows.Close()

var users []user
for rows.Next() {
    var u user
    err := rows.Scan(&u.id, &u.username, &u.password, &u.createdAt) // check err
    users = append(users, u)
}
err := rows.Err() // check err

users 切片应该包含如下所示内容:

users {
    user {
        id:        1,
        username:  "johndoe",
        password:  "secret",
        createdAt: time.Time{wall: 0x0, ext: 63701044325, loc: (*time.Location)(nil)},
    },
    user {
        id:        2,
        username:  "alice",
        password:  "bob",
        createdAt: time.Time{wall: 0x0, ext: 63701044622, loc: (*time.Location)(nil)},
    },
}

从表中删除一个用户

最后,从我们的表中删除用户就像上面部分中的 .Exec 一样直接:

1
_, err := db.Exec(`DELETE FROM users WHERE id = ?`, 1) // check err

完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
package main

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

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

func main() {
    db, err := sql.Open("mysql", "root:root@(127.0.0.1:3306)/root?parseTime=true")
    if err != nil {
        log.Fatal(err)
    }
    if err := db.Ping(); err != nil {
        log.Fatal(err)
    }

    { // Create a new table
        query := `
            CREATE TABLE users (
                id INT AUTO_INCREMENT,
                username TEXT NOT NULL,
                password TEXT NOT NULL,
                created_at DATETIME,
                PRIMARY KEY (id)
            );`

        if _, err := db.Exec(query); err != nil {
            log.Fatal(err)
        }
    }

    { // Insert a new user
        username := "johndoe"
        password := "secret"
        createdAt := time.Now()

        result, err := db.Exec(`INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)`, username, password, createdAt)
        if err != nil {
            log.Fatal(err)
        }

        id, err := result.LastInsertId()
        fmt.Println(id)
    }

    { // Query a single user
        var (
            id        int
            username  string
            password  string
            createdAt time.Time
        )

        query := "SELECT id, username, password, created_at FROM users WHERE id = ?"
        if err := db.QueryRow(query, 1).Scan(&id, &username, &password, &createdAt); err != nil {
            log.Fatal(err)
        }

        fmt.Println(id, username, password, createdAt)
    }

    { // Query all users
        type user struct {
            id        int
            username  string
            password  string
            createdAt time.Time
        }

        rows, err := db.Query(`SELECT id, username, password, created_at FROM users`)
        if err != nil {
            log.Fatal(err)
        }
        defer rows.Close()

        var users []user
        for rows.Next() {
            var u user

            err := rows.Scan(&u.id, &u.username, &u.password, &u.createdAt)
            if err != nil {
                log.Fatal(err)
            }
            users = append(users, u)
        }
        if err := rows.Err(); err != nil {
            log.Fatal(err)
        }

        fmt.Printf("%#v", users)
    }

    {
        _, err := db.Exec(`DELETE FROM users WHERE id = ?`, 1)
        if err != nil {
            log.Fatal(err)
        }
    }
}

本文翻译自:https://gowebexamples.com/mysql-database/

updatedupdated2021-07-032021-07-03
Load Comments?