普段は GAE で golang を使っているけど、
golang で mysql 使ったことないってのもどうなのかな? と思ったので、
ちょっとしたサンプルアプリを通して mysql を使ってみようと思った。
で、ORM どーしーよーかなと思って、
色々調べた記録です。
標準パッケージ
標準パッケージが良い感じであれば、ORM は不要だと思ったので確認してみた。標準パッケージでは database/sql を利用する。
https://golang.org/pkg/database/sql/
以下が使用例
https://github.com/golang/go/blob/master/src/database/sql/example_test.go
生のSQLを実行するのは問題ないんだけど、
Scan() で値を取り出していくのが面倒。
以下のように結果が複数レコードの場合に for で回すのも面倒。
https://github.com/golang/go/blob/master/src/database/sql/example_test.go#L77-L86
あと、struct とのマッピングは欲しい。
ということで、要件にもよるけど、
database/sql をそのまま利用するのはちょっと厳しいかな。
ちなみに、database/sql と database/sql を利用している ORM を利用するには
以下のドライバが必要になる。
https://github.com/golang/go/wiki/SQLDrivers
ドライバとdatabase/sqlの関係性は以下を読むとイメージできるかもしれない。
http://pospome.hatenablog.com/entry/2017/01/29/171904
ORMに求める要件
ORMを探すにあたって、個人的に以下の要件は必須。structにマッピングできる
これは欲しい。struct の tag とかでカラム指定したりするイメージ。
テーブル名とかstructに変な命名規則を適用させる必要がない
usersテーブルに対する struct は Users にする必要がある的なルールがあってもいーんだけど、それを struct の tag とか、interface とかで回避できる仕組みは欲しい。
生SQLが書ける
複雑なSQLをクエリビルダとかで書くのが面倒だったり、ORMがSQLの方言に対応してなかった時(mysql5.7のjson系の関数とか)に生SQLで書きたい。
その結果はもちろん struct にマッピングしたい。
以下は必須じゃないけど、気にするところ。
発行されるクエリがイメージできる
「裏側で妙なSQLが発行されてインデックスが効かない」「意図しないSQLが発行されている」
みたいなORMあるあるは回避したい。
association みたいなテーブルを良い感じに join する機能を明示的に利用しない限りこーゆーことはないと思うから、
そんなに気にする必要もないかもしれない。
パフォーマンスが極端に悪くない
早いに越したことはないが、どこまで速度にこだわるのかって問題になるから、正直どーでもいいというか、使い勝手とのトレードオフ。
どーしてもパフォーマンスが気になるなら、標準パッケージの database/sql 使えばいいと思うし、
どーでもいいなら、使い勝手を重視すればいい。
色々調べたが、これらを満たさないORMは基本なかったと思う。
なので、最終的には好みの問題になるのかもしれない。
ただ、パフォーマンスは実際に測定してないので分からない。
ネットに比較結果が転がっていたりするので、
気になるようであれば調べてみるといいかもしれない。
gorm
最初に調べたのが gorm というORMだった。https://github.com/jinzhu/gorm
ドキュメントは以下。
http://jinzhu.me/gorm/
他のORMに比べて多機能な印象を持った。
associations とかもある。
http://jinzhu.me/gorm/associations.html
これを使っておけば変に困ることもないと思う。
ちなみに、gorm を fork した ngorm というのがあるが、
現時点で mysql に対応していないので見送り。
https://github.com/ngorm/ngorm
gorp
次は gorp を調べた。https://github.com/go-gorp/gorp
「SELECTでは生SQLを書く必要がある」という点は独特だなと思った。
以下のようなイメージで、生SQLを書いて、それに struct をバインドする。
https://github.com/go-gorp/gorp/blob/master/gorp_test.go#L859
insert, update, delete は他のORMと同じ印象。
insert
https://github.com/go-gorp/gorp/blob/master/gorp_test.go#L432
update
https://github.com/go-gorp/gorp/blob/master/gorp_test.go#L564
delete
https://github.com/go-gorp/gorp/blob/master/gorp_test.go#L653
もう1点独特だなと思ったのは struct と DBテーブルのヒモ付を struct の tag, interface ではなく、
以下のようにコードで指定するところ。
struct が tag, interface で汚れないのは嬉しい。
https://github.com/go-gorp/gorp/blob/master/gorp_test.go#L1227-L1228
問題は SELECT WHERE IN() が使えない点。
使えないことはないけど、
gorpではSELECTをSQLで書く必要があるので、
以下のようにプレースホルダーを利用したSQLと
そのプレースホルダーに対応する 100, 200, 300 という値をバインドしていかないといけない。
err = dbmap.Select(&users, "select * from posts where id in (?, ?, ?)", 100, 200, 300)
そして、以下のように slice で指定できないのがキツイ。
slice を指定するとエラーになる。
err = dbmap.Select(&users, "select * from posts where id in (?, ?, ?)", []int{100,200,300})
これどーやって in で指定するパラメータを可変にするんでしょうね・・・。
issue にもなっているが、解決されていない・・・。
https://github.com/go-gorp/gorp/issues/85
自分はここが気になって gorp の利用を見送った。
*この記事のコメントにて slice を指定できることを教えてもらいました。
dbr
https://github.com/gocraft/dbrこれは以下の記事を見た方が早い。
https://eurie.co.jp/blog/engineering/2015/12/go-lang-ormapper-dbr
自分の要件を満たしていて良い感じだったが、なぜか timezone が UTC から JST に変更できなかった。
dbrというか、database/sql で利用するドライバのレイヤの話になると思うんだけど、
以下のように設定すれば timezone を変更可能なはずが、なぜか反映されない・・・。
https://note.mu/tomyhero/n/nc31c788bc7d8
http://kenzo0107.hatenablog.com/entry/2015/08/19/165310
他の ORM と比較して公式のドキュメントが弱い印象も受けた。
というか、他が頑張ってドキュメント作ってる気がする。
JST問題は頑張って解決してもよかったが、一旦他のORMを調べることにした。
xorm
最後に試したのがこれ。https://github.com/go-xorm/xorm
ドキュメントは以下。
http://xorm.io/docs/
エウレカさんで採用されているらしい。
https://developers.eure.jp/tech/go_web_application_1/
インメモリキャッシュを備えているのも珍しい。
実用に耐えるかは不明。
面白いと思ったのは
xormを利用すると wizard というシャーディング&レプリケーション用のライブラリも利用できる。
https://github.com/evalphobia/wizard
ちゃんと見てないし、使ってないから分からないんだけど、
生のSQLやクエリビルダは書けないっぽい????
https://github.com/evalphobia/wizard/blob/master/orm/xorm/interface.go#L13-L54
wizard に関しては、実際どの程度実用に耐えるのか不明。
この記事を書いた後に知ったORM
この記事を書いた後に知ったORMも載せておく。実際に使ったわけじゃないので、パッ見た感じの感想のみ。
sqlboiler
以下の記事で知った。https://qiita.com/gougyan/items/5295e4a30697a73868b5
sqlboiler
https://github.com/volatiletech/sqlboiler
以下によると、
ruby の ActiveRecord の生産性の高さを求めて作られたものっぽい。
https://github.com/volatiletech/sqlboiler#why-another-orm
そのため、
SQL のテーブル定義からモデルを自動生成するのが前提になっており、
そのモデルは ActiveRecord パターンを踏襲した操作感を提供している。
https://github.com/volatiletech/sqlboiler#find
https://github.com/volatiletech/sqlboiler#insert
おすすめは?
おすすめとしては gorm かな・・・。パフォーマンス面や使い勝手(これは個人差あるけど)は他の ORM に劣るかもしれないが、
多機能なので「あれができない」「これができない」で困ることはないし・・・。
gorm が合わなかったら、他の ORM を使ってみればいいと思う。
結局何を使ったのか?
なんとなく最後の xorm を使うことにした。今のところ、大きな問題もなく使えている。
素直に gorm にしておけばよかったのかもしれない・・・。
そーいえば、PHP の Cake, Fuel, ruby の rails のようなフルスタックなWAFは
それ用のORMが付属してるから、
こーやって色々調べる必要なくて楽だったな・・・。