Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

GORMがv2になっていたので、変更点を調べてみる

はじめに

golangでプロトタイプを作成していて、いつも通り GORM を利用しようとした際、どうせなら最新版でと思ったところ、 大きく変わっていました。
詳細は、v2のリリースノートを見てもらうのが一番ですが、変更点も多いため、単純にバージョンアップする際にひっかかりそうなところを調べてみました。

インストール

この記事の段階での最新バージョンは、1.20.8 になりますが、
1.20系にバージョン番号が上がると、インストールする際のパスが変更されます。
いままでは

go get -u github.com/jinzhu/gorm

となっていましたが、 v2になって

 go get -u gorm.io/gorm

ドキュメントも https://v1.gorm.iohttps://gorm.io/ に分かれています。

Open

初期化する際の関数も引数が変更されています。

v1

func Open(dialect string, args ...interface{}) (db *DB, err error) 

第1引数が、"mysql""postgres"といった接続するRDBMSの種類を文字列で指定し、第2引数に接続文字列を渡します。
また、必要なDriverパッケージをブランク識別子を使用してインポートする必要があります。

import (
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  db, err := gorm.Open("mysql", "test:test@/testdb")
  ...
}

v2

func Open(dialector Dialector, config *Config) (db *DB, err error)

第1引数に gorm.Dialector という型で渡すように変更されています。
こちらは接続するRDBMSの種類に応じて、gorm用のDriverを初期化して渡す必要があります。
v2の場合は、ブランク識別子は不要になります。
また、第2引数のConfigでいろいろと細かい設定や機能が追加されていますが、今回は省略します。

import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)

func main() {
  db, err := gorm.Open(mysql.Open("test:test@/testdb"), &gorm.Config{})
  ...
}

DeletedAt

論理削除をしたい場合、v1では DeletedAt フィールドを *time.Timeで用意していましたが、
v2では、フィールド名ではなく、gorm.DeletedAt という型で定義する必要があります。

v1
type Example struct {
...

  DeletedAt *time.Time
}
v2
type Example struct {
...

  Deleted gorm.DeletedAt
}

Hooks

実際にSQLが発行される前後のHook処理もインターフェースの条件が変更されています。

v1

DOMに下記の名前のメソッドを実装することで処理を呼ぶことができます。

BeforeSave
BeforeCreate
AfterCreate
AfterSave

v2

メソッド名に変更はありませんが、HooksThe type of hook methods should be func(*gorm.DB) error 記述にあるように
それぞれのメソッドの引数に *gorm.DB を渡す必要があります。
メソッドの型が異なると、実際の処理時には実行されないため注意が必要です。

RecordNotFound

Selectを発行して該当レコードがない場合に、db.RecordNotFound() メソッドで判定していましたが、
当該メソッドがなくなりました。errors.Is()を使用して、判定を行う必要があります。

v1
db = db.First(entity)
if db.RecordNotFound() {
  ...
}
v2
db = db.First(entity)
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
   ...
}

ログ出力

デバッグログの出力設定が変わりました。

v1

LogModeをtrueに設定するだけで、SQLがログ出力されます。

v1
db.LogMode(true)

v2

Logger を設定する必要があります。
新しくGORM用のLoggerが提供されており、出力レベルが変更できるようになりました。
レベルをInfo にすることで、SQLのログ出力が行われます。

logger/logger.go
const (
    Silent LogLevel = iota + 1
    Error
    Warn
    Info
)
v2
import (
  "gorm.io/gorm"
  "gorm.io/gorm/logger"
)

...
db.Logger = db.Logger.LogMode(logger.Info)
...

DB()

地味な変更ですが、v1の時には、 *gorm.DB の DB()メソッドを呼ぶことで、*sql.DB だけを取得することができましたが、
v2においては、errorも返すようになりました。

v1
db.DB().SetMaxIdleConns(10)
v2
if sqlDb, err := db.DB(); err != nil {
  panic(err)
} else {
  sqlDB.SetMaxIdleConns(10)
}

Close

v1においては*gorm.DBにCloseメソッドがありましたが、v2ではなくなりました。
v2では、*sql.DBを取得してCloseを実行する必要があります。

v1
 db.Close()
v2
if sqlDB, err := db.DB(); err != nil {
  ...
} else {
  if err := sqlDB.Close(); err != nil {
     ...
  }
}

まとめ

上にあげた点だけではなく、マイグレーションや、バッチ更新など、かなり機能が追加されています。
これから新しく作るプロダクトにおいては、最新版を使うことで、他のライブラリを探す手間がはぶけるというところでしょうか。
すでに動いているものを急いで更新するメリットはあまり感じません。
それでもバージョンアップを行う場合には、単にビルドが通ればいいというわけではなく、
ビルド時のエラーがでない変更も多いため、事前の動作確認を確実に行う必要があります。

earl2
openlogi
物流はこれから、テクノロジーによりダイナミックに変化する業界です。物を作る人とそれを欲しい人、その間の物流や配送がすべてネットワーク化された、需要と供給が最適化される次世代のインフラづくりを私たちは目指しています。
https://openlogi.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away