四、GO Web编程示例 - MySQL数据库

简介

此示例将研究GO语言中MySQL数据库访问的基础知识,创建数据库表,存储和读取数据。

安装go-sql-driver / mysql软件包

Go编程语言附带了一个名为database / sql的软件包,用于查询各种SQL数据库。 这很有用,因为它将所有通用SQL功能抽象为一个API供你使用,但 Go不包括数据库驱动程序。 在Go中,数据库驱动程序是一个用于实现特定数据库底层细节的软件包(本例为MySQL)。由于无法预见哪些数据库将来都会投入使用,而支持每个可能的数据库将需要大量维护工作,因此Go语言未包含数据库驱动程序,需要额外进行安装。
要安装MySQL数据库驱动程序,只需要在终端中执行以下操作:

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

连接到MySQL数据库

安装所有必需的软件包后,需要检查的第一件事是,是否可以成功连接到MySQL数据库。 如果尚未运行MySQL数据库服务器,可以通过Docker轻松的启动一个实例。 这是MySQL的映像的官方Docker镜像:https://hub.docker.com/_/mysql
要检查是否可以连接到数据库,请导入数据库/ sql和go-sql-driver / mysql软件包,并按如下所示打开连接:

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命令如下:

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数据库中创建表:

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驱动程序,它们是实际数据的占位符。 在这里可以看到准备好的语句。

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

现在可以在Go中使用此语句插入用户。

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,只需按以下方式获取:

userID, err := result.LastInsertId()

查询用户表

现在表中有一个用户,我们想查询它并获取其所有信息。 在Go中有两种查询表的方法。 db.Query可以迭代查询多行;还有db.QueryRow只查询特定的行。
查询特定行的工作原理基本上与之前介绍的所有其他SQL命令一样。
SQL命令通过其ID查询单个用户,如下所示:

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

在Go中首先声明一些变量来存储数据,然后查询单个数据库行,如下所示:

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子句。 这样可以查询所有用户。

SELECT id, username, password, created_at FROM users

在Go中首先声明一些变量来存储数据,然后查询单个数据库行,如下所示:

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

user切片现在包含的内容如下:

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一样简单:

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

代码

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)
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356