まずは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もありますが、私にはこれくらいがほどよい)
※コードが見やすいようにエラー処理は省略してます