# 使用 MySQL 資料庫

目前 Internet 上流行的網站構架方式是 LAMP，其中的 M 即 MySQL, 作為資料庫，MySQL 以免費、開源、使用方便為優勢成為了很多 Web 開發的後端資料庫儲存引擎。

## MySQL 驅動

Go 中支援 MySQL 的驅動目前比較多，有如下幾種，有些是支援 database/sql 標準，而有些是採用了自己的實現介面，常用的有如下幾種:

* <https://github.com/go-sql-driver/mysql>  支援 database/sql，全部採用 go 寫。
* <https://github.com/ziutek/mymysql>   支援 database/sql，也支援自訂的介面，全部採用 go 寫。
* <https://github.com/Philio/GoMySQL> 不支援 database/sql，自訂介面，全部採用 go 寫。

接下來的例子我主要以第一個驅動為例(我目前專案中也是採用它來驅動)，也推薦大家採用它，主要理由：

* 這個驅動比較新，維護的比較好
* 完全支援 database/sql 介面
* 支援 keepalive，保持長連線，雖然 [ 星星](http://www.mikespook.com)fork 的 mymysql 也支援 keepalive，但不是執行緒安全的，這個從底層就支援了 keepalive。

## 範例程式碼

接下來的幾個小節裡面我們都將採用同一個資料庫表結構：資料庫 test，使用者表 userinfo，關聯使用者資訊表 userdetail。

```sql
CREATE TABLE `userinfo` (
    `uid` INT(10) NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(64) NULL DEFAULT NULL,
    `department` VARCHAR(64) NULL DEFAULT NULL,
    `created` DATE NULL DEFAULT NULL,
    PRIMARY KEY (`uid`)
);

CREATE TABLE `userdetail` (
    `uid` INT(10) NOT NULL DEFAULT '0',
    `intro` TEXT NULL,
    `profile` TEXT NULL,
    PRIMARY KEY (`uid`)
)
```

如下範例將示範如何使用 database/sql 介面對資料庫表進行增刪改查操作

```go
package main

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

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

func main() {
    db, err := sql.Open("mysql", "astaxie:astaxie@/test?charset=utf8")
    checkErr(err)

    //插入資料
    stmt, err := db.Prepare("INSERT userinfo SET username=?,department=?,created=?")
    checkErr(err)

    res, err := stmt.Exec("astaxie", "研發部門", "2012-12-09")
    checkErr(err)

    id, err := res.LastInsertId()
    checkErr(err)

    fmt.Println(id)
    //更新資料
    stmt, err = db.Prepare("update userinfo set username=? where uid=?")
    checkErr(err)

    res, err = stmt.Exec("astaxieupdate", id)
    checkErr(err)

    affect, err := res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    //查詢資料
    rows, err := db.Query("SELECT * FROM userinfo")
    checkErr(err)

    for rows.Next() {
        var uid int
        var username string
        var department string
        var created string
        err = rows.Scan(&uid, &username, &department, &created)
        checkErr(err)
        fmt.Println(uid)
        fmt.Println(username)
        fmt.Println(department)
        fmt.Println(created)
    }

    //刪除資料
    stmt, err = db.Prepare("delete from userinfo where uid=?")
    checkErr(err)

    res, err = stmt.Exec(id)
    checkErr(err)

    affect, err = res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    db.Close()

}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
```

透過上面的程式碼我們可以看出，Go 操作 Mysql 資料庫是很方便的。

關鍵的幾個函式我解釋一下：

sql.Open()函式用來開啟一個註冊過的資料庫驅動，go-sql-driver 中註冊了 mysql 這個資料庫驅動，第二個參數是 DSN(Data Source Name)，它是 go-sql-driver 定義的一些資料庫連結和配置資訊。它支援如下格式：

```
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
```

db.Prepare()函式用來回傳準備要執行的 sql 操作，然後回傳準備完畢的執行狀態。

db.Query()函式用來直接執行 Sql 回傳 Rows 結果。

stmt.Exec()函式用來執行 stmt 準備好的 SQL 語句

我們可以看到我們傳入的參數都是 =? 對應的資料，這樣做的方式可以一定程度上防止 SQL 注入。

## links

* [目錄](https://github.com/doggy8088/build-web-application-with-golang-zhtw/tree/4cbbaa31bdfc3678915eb91f23db2f1bad554a20/preface.md)
* 上一節: [database/sql 介面](/build-web-application-with-golang-zhtw/05.0/05.1.md)
* 下一節: [使用 SQLite 資料庫](/build-web-application-with-golang-zhtw/05.0/05.3.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://willh.gitbook.io/build-web-application-with-golang-zhtw/05.0/05.2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
