goでmysqlを使うメモ

久しぶりだー

前回の日記が2014年11月3日…だいぶ放置してたなorz

goでmysqlを使ってみる

mysqlドライバをインストール

database/sqlmysqlドライバとして go-sql-mysql というのがある。まずはこれを go get でインストールする。
github.com

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

今回使うサンプル

接続先DB
データベース名 mydatabase
ユーザ user1
パスワード password1
テーブル(member)
id int(11) NO PRI NULL auto_increment
name varchar(100) NO NULL
birthday varchar(5) YES NULL
blood_type char(2) YES NULL
hobby varchar(100) YES NULL

準備ができたら早速コードを書いてみる。サンプル全体は以下に置いてあります。
hatena_blog/sample.go at master · egawata/hatena_blog · GitHub

レコードの内容を格納するstructの宣言

データの格納先はスカラー変数でも十分なのだが、一応見やすさのため member テーブルの列に対応した struct を用意しておく。

type Member struct {
    Id        int
    Name      string
    Birthday  string
    Bloodtype string
    Hobby     string
}

importする

最低限以下のパッケージが必要

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

go-sql-driver を import するが、mainパッケージから直接参照することはないので、先頭に _ をつけて blank import とする。

接続

sql.Open()を使う。

引数としてDSNを指定する必要があるが、フォーマットが若干ややこしい。

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]

ローカルホストで起動しているmysqlに接続するなら以下でOK。

username:password@/dbname

外部ホストのmysqlTCP接続して使う場合は以下のようなDSNになる。

user1:password1@tcp(192.168.1.10:3306)/mydatabase

go-sql-driverのドキュメントの下のほうに、様々なシチュエーションでのDSNの例が載っているので、迷ったら参考にするといいと思う。

結果的に以下のようなコードとなった。

var Db *sql.DB
var err error

Db, err = sql.Open("mysql", "user1:password1@tcp(127.0.0.1:3306)/mydatabase?charset=utf8")
defer Db.Close()

INSERT実行

prepareして

    stmt, err := Db.Prepare(`
        INSERT INTO member (name, birthday, blood_type, hobby)
        VALUES (?, ?, ?, ?)
    `)
    if err != nil {
        return
    }
    defer stmt.Close()

実行。

        ret, err = stmt.Exec(m.Name, m.Birthday, m.Bloodtype, m.Hobby)

(説明雑だな)


INSERTされた列のIDを知りたいときは、戻り値 ret(Result型) の LastInsertId() を実行すればよい。int64型で返ってくるので、必要であればキャストする。

id, err = ret.LastInsertId()
m.Id = int(id)

SELECT実行(単一レコードを取得)

database/sql には Query() と QueryRow() という2つの関数が用意されているが、結果が単一レコードであることを期待している場合は QueryRow() を使うとよい。

    member = Member{}

    err = Db.QueryRow(`
        SELECT id, name, birthday, blood_type, hobby
          FROM member
         WHERE id = ?
    `, id).Scan(&member.Id, &member.Name, &member.Birthday, &member.Bloodtype, &member.Hobby)

QueryRow() の引数はSQL文とバインド値。
Scan()の引数は結果列の値を格納する変数のアドレス。取得される列の数と一致している必要がある。

なお取得されうるレコードが2つ以上の場合は、Scan() で最初の1レコードの値のみが取得される。
逆に1行も取得されなかった場合は、Scan() は ErrNoRows を error として返す。このときのエラー処理を他のエラーと区別したい場合は以下のようにする。

    if err == sql.ErrNoRows {           //  見つからなかった
        log.Fatalln("そのIDのメンバーは存在しません")
    } else if err != nil {                      // それ以外のエラー
        log.Fatalln(err)
    }

SELECT実行(複数レコードを取得)

結果が複数レコードになることが期待される場合は Query() を使う。

func getAllMembers() (members []Member, err error) {
    rows, err := Db.Query("SELECT id, name, birthday, blood_type, hobby FROM member")
    if err != nil {
        return
    }

    for rows.Next() {
        m := Member{}
        err = rows.Scan(&m.Id, &m.Name, &m.Birthday, &m.Bloodtype, &m.Hobby)
        if err != nil {
            return
        }
        members = append(members, m)
    }
}

結果として返ってきた rows(*Rows型)を使い、rows.Next()でループさせ、rows.Scan()で各列の値を受け取る。