RubyのActive Recordを使っていた人がGolangのORM【GORM】に入門した話
前田です。
最近はGolangを書いているのですが、ORMのGORMの使い勝手が良かったので、RubyのActive Record(以下AR)を使っていた人から見た感想を書きたいと思います。
Go 1.8.3
GORM 1.0
DB MySQL
新規作成
まずデータの作成。
AR
user = User.create(name: 'hoge')
または
user = User.new(name: 'hoge')
user.save
GORM
user := User{Name: "hoge"}
db.Create(&user)
定番の Create
です。
ARはsave
もあります。
更新
続いてデータの更新。
AR
user.update(name: 'hogehoge')
GORM
user.Name = "hogehoge"
db.Save(&user)
または
db.Model(&user).Update("name", "hoge")
db.Model(&user).Updates(User{Name: "hogehoge"})
ARはupdate
なのに対し、GORMはSave
とUpdate
があります。
GORMのSave
とUpdate
の違いは、Save
は他のカラムも合わせて更新するのに対し、Update
は対象のカラムのみの更新になることです。
たとえばusersテーブルに、name
email
というカラムがあった場合、Save
では
UPDATE users SET name='hogehoge', email='hoge@example.com' WHERE id=1;
というクエリが発行され、Update
は、
UPDATE users SET name='hogehoge' WHERE id=1;
というクエリが発行されるということです。
削除
AR
user.destroy
または
user.delete
GORM
db.Delete(&user)
ほとんど同じですね。
gorm.Model
を使っている場合、db.Delete(&user)
は論理削除になるので物理削除をしたい場合、
db.Unscoped().Delete(&user)
になります。
検索
続いて検索。
最初の1件取得。
AR
User.first
GORM
db.First(&user)
最後の1件取得。
AR
User.last
GORM
db.Last(&user)
ほとんど一緒!これはAR使いにとっては嬉しいですね。
思わず、
db.Second(&user)
とやってしまいそうです(笑)。
続いて条件検索。
AR
User.where(name: 'hoge')
User.where(name: ['hoge', 'hogehoge'])
User.where('name like ?', 'hoge')
# クエリチェーン
User.where(name: 'hoge').where(name: 'hogehoge')
GORM
db.Where("name = ?", "hoge").Find(&users)
db.Where("name in (?)", []string{"hoge", "hogehoge"}).Find(&users)
db.Where("name like ?", "%hoge%").Find(&users)
// クエリチェーン
db.Where("name like ?","hoge").Where("name = ?", "hogehoge").Find(&users)
これもほとんど一緒ですね。
GORMのFind
は複数取得(ARの複数取得はwhere
ですが)、First
は1件取得、なのもARに感覚的にすごく近いです。
あとGORMはOR検索にすでに対応しています。
ARでは結構最近追加された機能なのでGORM早いですね。
テーブル結合
AR
User.joins('JOIN credit_cards ON credit_cards.user_id = users.id').where('credit_cards.number = ?', '411111111111')
GORM
db.Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
これもほとんど同じでした。
指定したカラムだけ取得
AR
User.pluck(:name)
GORM
var names []string
db.Model(&User{}).Pluck("name", &names)
まさかGORMにpluck
があるとは!
ただし、ARでは、
User.pluck(:name, :email)
で、複数のカラムを纏めて取得できましたが、GORMでは1つだけのようです。
纏めて取得したい場合はSelect
を使って、
db.Select("name, name").Find(&users)
とやります。
スコープ
続いて検索条件などを部品にして使用出来るスコープです。
AR
class User < ActiveRecord::Base
scope :active, -> { where('active = TRUE') }
scope :by_name, lambda { |name|
return if name.blank?
where('name = ?', name)
}
end
User.active.by_name('hoge')
GORM
func Active() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("active = TRUE")
}
}
func ByName(name string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if name == "" {
return db
}
return db.Where("name = ?", name)
}
}
result := db.Scopes(
Active(),
ByName("hoge"),
).Find(&users)
スコープがあると検索ロジックをスコープに閉じ込めることが出来るので、ソースコードの可読性・メンテナンス性が格段によくなります。
よくあるのが、クライアントから渡ってくる任意パラメータで検索するという機能ですが、パラメータがあるかないかが分からないので普通にロジックを書くとすべての分岐を書くか、メタプログラミングでソースコードを動的に生成するしかありません。
それがスコープを使うと上記のように簡単にパラメータがある時だけ検索するロジックを簡単に組むことが出来ます。
GORMにスコープがあってほんとに良かったです。
ARのスコープはモデル(クラス)に紐付いたものになりますが、GORMはモデル(構造体)に紐付いていないスコープなので、name
カラムがあればどんなモデルにも使うことが出来ます。
result := db.Scopes(
ByName("admin"),
).Find(&administrators)
これはARよりもいいですね。
キャッシュ
AR
User.preload(:credit_cards)
User.includes(:credit_cards)
GORM
db.Preload("CreditCards").Find(&users)
ARはpreload
やincludes
などいくつかありますが、GORMはまだ1つだけみたいです。
いかがでしたでしょうか?
私は作った人が同じなんじゃないか、と思うほど2つは似ていると感じましたが皆さんはどうでしょうか。
まだちょっとしかGORMを触っていないのですが、ARと雰囲気がかなり似ていて、AR使いの人はあまり抵抗無くすんなり書いていけるORMなんじゃないかなと思いました。
シンプルに使えるGORM、RubyのActive Recordが好きな人とは相性が良さそうです。
Document : GORM