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はSaveUpdateがあります。
GORMのSaveUpdateの違いは、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 { |car_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はpreloadincludesなどいくつかありますが、GORMはまだ1つだけみたいです。


いかがでしたでしょうか?
私は作った人が同じなんじゃないか、と思うほど2つは似ていると感じましたが皆さんはどうでしょうか。
まだちょっとしかGORMを触っていないのですが、ARと雰囲気がかなり似ていて、AR使いの人はあまり抵抗無くすんなり書いていけるORMなんじゃないかなと思いました。
シンプルに使えるGORM、RubyのActive Recordが好きな人とは相性が良さそうです。

Document : GORM

このエントリーをはてなブックマークに追加