goでmysqlを使う

http://github.com/go-sql-driver/mysql
がありましたので、それを使います。

以下のように読み込むことで、sql.Openでmysqlを開くことが出来ます。

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

DB設定

以下の用に指定する事で、ローカルのmysqlの指定したデータベースにアクセス出来ます。

db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
  panic(err.Error())
}
defer db.Close()

サーバやデータベース名などはDSN (Data Source Name)で指定するようです。
あまり聞かない方法ですが、公式のREADMEに書いてあるのでそれを参考にすると良いと思います。

使い方

前提条件

上記の方法でsql.Openの結果を変数のdbに保存済み、
以下の構造体をDBに書き込むとします。

type Post struct {
  RoomName string
  Message string
  MessageId string
  IsSend bool
}

また、tableNameに書き込むテーブル名が保存されているとします。

INSERT

post := getPost() // 書き込むためのデータを取得する

stmtIns, err := db.Prepare(fmt.Sprintf("INSERT INTO %s (room_name, message, message_id, is_send) VALUES (?, ?, ?, ?)", tableName))
if err != nil {
  panic(err.Error())
}
defer stmtIns.Close() // Close the statement when we leave main() / the program terminates

_, err = stmtIns.Exec(post.RoomName, post.Message, post.MessageId, post.IsSend)

db.PrepareでSQL文を用意し、Execの引数にプレースフォルダの数だけ書き込むデータを渡しています。
テーブル名をプレースホルダにするなど、SQLの文法的におかしい場合は、db.Prepareの戻り値がエラーになります。

UPDATE

stmtIns, err := db.Prepare(fmt.Sprintf("UPDATE %s SET is_send = 1 WHERE ( message_id = ?)", tableName))
if err != nil {
  panic(err.Error())
}
defer stmtIns.Close()

_, err = stmtIns.Exec(post.MessageId)

INSERTとほぼ同じです。

SELECT

stmtOut, err := db.Prepare(fmt.Sprintf("SELECT room_name, message, message_id, is_send FROM %s WHERE message_id = ? LIMIT 1", tableName))
if err != nil {
  panic(err.Error())
}
defer stmtOut.Close()

var room_name string
var message string
var message_id string
var is_send bool
if err := stmtOut.QueryRow(searchMessageId).Scan(&room_name, &message, &message_id, &is_send); err != nil {
  return nil
}
var postData Post
postData.RoomName = room_name
postData.Message = message
postData.MessageId = message_id
postData.IsSend = is_send

ORマッパーではないので、一つ一つ取り出した値をオブジェクトにセットしていく必要があります。

複数SELECT

上の例はLIMIT 1を設定して1件だけ取得していましたが、複数取得の場合はscanではうまきいきません。 複数取り出す場合はこちらです。

rows, err := db.Query(fmt.Sprintf("SELECT room_name, message, message_id, is_send FROM %s WHERE is_send = false LIMIT 100", tableName))
if err != nil {
  panic(err.Error()) // proper error handling instead of panic in your app
}

posts := make([]*Post, 0)

for rows.Next() {
  var room_name string
  var message string
  var message_id string
  var is_send bool
  if err := rows.Scan(&room_name, &message, &message_id, &is_send); err != nil {
  }

  var postData Post
  postData.RoomName = room_name
  postData.Message = message
  postData.MessageId = message_id
  postData.IsSend = is_send
  posts = append(posts, postData)
}

Nextを利用して一件ずつScanしています。

まとめ

dp.Prepareでプレースホルダー付きのSQL文を作成。
INSERTやUPDATEはExecで実行、SELECTはQueryやQueryRowで実行してScanでデータを取り出します。
テーブル構造が変わると読み込み部分が変わったり、順番を間違えると悲惨なことになるため面倒です。
ORマッパーは偉大ですね…