久しぶりだー
前回の日記が2014年11月3日…だいぶ放置してたなorz
goでmysqlを使ってみる
mysqlドライバをインストール
database/sql の mysqlドライバとして 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
外部ホストのmysqlにTCP接続して使う場合は以下のような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()で各列の値を受け取る。