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

# はじめに
  golangでプロトタイプを作成していて、いつも通り `GORM` を利用しようとした際、どうせなら最新版でと思ったところ、 大きく変わっていました。
 詳細は、[v2のリリースノート](https://gorm.io/docs/v2_release_note.html)を見てもらうのが一番ですが、変更点も多いため、単純にバージョンアップする際にひっかかりそうなところを調べてみました。
# インストール
 この記事の段階での最新バージョンは、`1.20.8` になりますが、
 1.20系にバージョン番号が上がると、インストールする際のパスが変更されます。
 いままでは

```golang
go get -u github.com/jinzhu/gorm
```
 となっていましたが、 v2になって

```golang
 go get -u gorm.io/gorm
```
ドキュメントも [https://v1.gorm.io](https://v1.gorm.io) と [https://gorm.io/](https://gorm.io/) に分かれています。

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

## v1
```golang
func Open(dialect string, args ...interface{}) (db *DB, err error) 
```
第1引数が、`"mysql"`や`"postgres"`といった接続するRDBMSの種類を文字列で指定し、第2引数に接続文字列を渡します。
また、必要なDriverパッケージをブランク識別子を使用してインポートする必要があります。

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

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


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

```golang
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` という型で定義する必要があります。

```golang:v1
type Example struct {
...

  DeletedAt *time.Time
}
```
```golang:v2
type Example struct {
...

  Deleted gorm.DeletedAt
}
```

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

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

```
BeforeSave
BeforeCreate
AfterCreate
AfterSave
```

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

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

```golang:v1
db = db.First(entity)
if db.RecordNotFound() {
  ...
}
```

```golang:v2
db = db.First(entity)
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
   ...
}
```


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

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

```golang:v1
db.LogMode(true)
```

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

```golang:logger/logger.go
const (
	Silent LogLevel = iota + 1
	Error
	Warn
	Info
)
```

```golang: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も返すようになりました。

```golang:v1
db.DB().SetMaxIdleConns(10)
```

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

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

```golang:v1
 db.Close()
```

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

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