Go
SQL
golang
Database
ORM

GoでMySQLにアクセスしてみる(gorp編)

まずはGoの標準パッケージでアクセスしてみる

GoにはRDBにアクセスするためのdatabase/sqlが標準で用意されています
普通に使用するのには特に問題はありません

Query

// 接続 
db, err := sql.Open("mysql", "user:password@tcp(xx.xx.xx.xx:3306)/db?parseTime=true")
defer db.Close()

// Select
rows, err := db.Query("select * from user")
defer rows.Close()

// 構造体へマッピング
for rows.Next() {
    user := User{}
    err := rows.Scan(&user.Id, &user.Name)
}

Insert

_, err = db.Exec("insert into user values (?, ?, ?) ", 1, "hoge", 30)

SelectのSQLを発行する部分はシンプルで良いが、構造体に値をマッピングする処理が煩雑
Insertでは構造体を直接追加したいのにSQLで書くのは煩雑
もっとスッキリ書きたい

そこでORM(gorp)を使ってアクセスしてみる

gorp(Go Relational Persistence)
https://github.com/go-gorp/gorp

gorpについて(特徴など)
- GolangのORMの一つ
- SelectはSQLを直書き。複雑なQueryとかも自由に直接かけるのでORMでよくあるパフォーマンスがでないとか気にしなくて良い
- 取得結果と構造体とのマッピングはORMがやってくれる
- Insert,Update,DeleteはSQL書かなくても良い

Select

// 接続 
db, err := sql.Open("mysql", "user:password@tcp(xx.xx.xx.xx:3306)/db?parseTime=true")
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
defer dbmap.Db.Close()

// Select複数行
var users []User
_, err := dbmap.Select(&users, "select * from user order by id")

// Select単一行
var user1 User
err = dbmap.SelectOne(&user1, "select * from user where id=?", 1)

// PKで取得
obj, err := dbmap.Get(User{}, 1)
user2 := obj.(*User)


接続文字列に?parseTime=trueを書くと日時をGolangのtimeにParseしてくれる
構造体へのマッピングはgorpが行ってくれるのでポインタを渡すのみ
簡単ですね

構造体とのマッピング定義

type User struct {
    Id        int64      `db:"id, primarykey"`
    Name      string     `db:"name"`
    Age       int64      `db:"age"`
}

タグでマッピングするカラム名を指定できるので、構造体のプロパティ名と異なっていても大丈夫
テーブル名が構造体の名前と異なっている場合は下記コードでマッピングが必要
Insert/Updateを利用する場合はprimarykeyでPKを指定します
autoincrementとかも指定可能

dbmap := dbmap.AddTableWithName(User{}, "user").SetKeys(false, "Id")

コードでテーブルのマッピングを指定する。コードでprimarykeyの指定も可能
テーブル名を構造体に合わせれば問題ないのですが、異なっている場合はdbmapを生成する毎に書かなければいけないので、なんとかならないかな。。

Insert/Update/Delete

user1 := &User{1, "ほげ 太郎", 23}
user2 := &User{2, "ふが 二郎", 41}

// Insert
err := dbmap.Insert(user1, user2)

// Update
user1.Age=42
count, err := dbmap.Update(user1)

// Delete
count, err := dbmap.Delete(user1)

Transaction

トランザクションも使えます

// Begin
tx, err := dbmap.Begin()

//(省略)...
tx.Insert(user1)
tx.Insert(user2)

// Commit
tx.Commit()

その他の機能

  • Create/Drop Tables
    • 構造体からDBをマイグレーションする機能、構造体のタグで桁数とかも指定できるし、Indexも定義できる
  • Hooks
    • Insert,Update直前に処理を追加。作成日時、更新日時を設定したりするのに使える
  • Optimistic Locking
    • versionカラムを追加することで楽観的排他をORMが行ってくれる。RailsのActiveRecordみたいですね

ハマりポイント

  • import _ "github.com/go-sql-driver/mysql"でドライバーを初期化は忘れずに
  • 構造体のプロパティのマッピングはタグでできるがテーブルとのマッピングはコードでしかできないので、毎回書くしかない
  • 日付を扱う場合は接続文字列にparseTime=trueを書きましょう

所感

  • gorpは構造体にマッピングしてくれるため、すっきりコードが書ける
  • SQLを直接書けるので学習コストの低く、ORMにありがちがパフォーマンス問題(ORMが発行するSQLが遅い)も回避できる
  • やろうと思えがDBマイグレーションや楽観的排他等RailsのActiveRecordのような機能も使える
  • gorpは機能と使い勝手がほどよいORMだと思いました(ActiveRecordのような多機能なORMもありますが、私にはこれくらいがほどよい)

※コードが見やすいようにエラー処理は省略してます