GORM v1.0からv2.0への移行時のハマりポイント(v1とv2の変更点)

# 概要

* 以前、GORM v1.0からv2.0にアップグレードをしたのですが、その際ハマったポイントとどんな変更があるのかについてまとめていきます。

## ハマりポイント

* GORM v2.0は仕様変更がかなり多く、すんなりv1.0からv2.0に移行できません。
* かくいう私もGORMのアップグレードをした際ちゃんと調べなかったので、テストコードを書いている時に挙動のおかしさに気づいてから原因解明までに時間がかかってしまいました。。
* 実際に私がぶち当たったハマりポイントというのが、外部キーによる参照をした際に、正しく外部キーの参照が行えておらず期待値とは違うSQLが発行されていました。

### 変更点1

* 外部キーによる参照を行う際にforeignkey, association_foreignkeyの指定があったのですが、この書き方が変わり、foreignkeyとreferencesを使った書き方に変更されました。

**v1**
```go
`gorm:"foreignkey:hoge;association_foreignkey:huga;" json:"id"`
```

**v2**
```go
`gorm:"foreignKey:hoge;references:huga;" json:"id"`
```

## v1とv2の変更点

* ここからは、他に仕様変更があったポイントをまとめていきます。

---

* GORMのgorm.DBオブジェクトを取得するgorm.Open関数の引数が変更になりました。
* 第一引数のdialectを文字ではなくgorm.Dialectorというオブジェクトで受けるようになったようです。

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

**v2**
```go
func Open(dialector Dialector, opts ...Option) (db *DB, err error)
```

ちなみに、Dialectorのinterfaceは下記のようになっています。

```go
type Dialector interface {
	Name() string
	Initialize(*DB) error
	Migrator(db *DB) Migrator
	DataTypeOf(*schema.Field) string
	DefaultValueOf(*schema.Field) clause.Expression
	BindVarTo(writer clause.Writer, stmt *Statement, v interface{})
	QuoteTo(clause.Writer, string)
	Explain(sql string, vars ...interface{}) string
}
```

* また、第2引数以降の指定方法も変更されています。
* なお第2引数以降に指定するOptionのinterfaceは下記ようになっています。

```go
type Option interface {
	Apply(*Config) error
	AfterInitialize(*DB) error
}
```

---

* Debugモードの書き方が変更になりました。

**v1**
```go
db.LogMode(true)
```

**v2**
```go
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
	Logger: logger.Default.LogMode(logger.Info),
})

もしくは
db = db.Debug()
```

---

* Related関数がV2からは、使用できなくなりました。

**v1**
```go
func (s *DB) Related(value interface{}, foreignKeys ...string) *DB {
    return s.NewScope(s.Value).related(value, foreignKeys...).db
}
```

---

* association_jointable_foreignkeyの指定方法が変更になりました。

**v1**
```go
`gorm:"jointable_foreignkey:hoge;association_jointable_foreignkey:huga;" json:"ids"`
```

**v2**
```go
`gorm:"joinForeignKey:hoge;joinReferences:huga;" json:"ids"`
```

---

* 主キー(PrimaryKey)の指定方法が変更になりました。

**v1**
```go
`gorm:"primary_key" json:"id"`
```

**v2**
```go
`gorm:"primaryKey" json:"id"`
```

---

* RecordNotFound()メソッドが廃止になりました.

**v1**
```go
func RecordNotFound(err error) bool {
    return errors.Is(err, gorm.ErrRecordNotFound)
}
```

---

* テーブル名の取得方法が変更になりました。

**v1**
```go
var db *gorm.DB
var users orm.Users

pretty.Println(db.NewScope(users).TableName())
```

**v2**
```go
var db *gorm.DB
var users orm.Users

stmt := &gorm.Statement{DB: db}
stmt.Model = users
stmt.Parse(stmt.Model)
pretty.Println(stmt.Table)
```

---

* RollbackUnlessCommittedのようなトランザクションメソッドが削除されました。
* なので、v2からは下記のように書く必要があります。

**v2**
```go
db.Transaction(func(tx *gorm.DB) error {
  // do some database operations in the transaction (use 'tx' from this point, not 'db')
  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
    // return any error will rollback
    return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
    return err
  }

  // return nil will commit the whole transaction
  return nil
})
```

---

* Set("gorm:auto_preload", true) でのpreloadができなくなりました。
* v2.0からは下記のように書く必要があります。

**v2**
```go
// preload all associations
db.Preload(clause.Associations).Find(&users)
```

# まとめ

* 今回は、GORM v1.0からv2.0へ移行する際にはまったポイントと、仕様変更があった箇所についてまとめていきました。(※ **完璧に仕様変更箇所全てをピックアップしているわけではないので、その点はご注意ください。**)
* アップグレードをする際の助けになれば幸いです。
* 最後までありがとうございました。

# 参考

https://gorm.io/ja_JP/docs/v2_release_note.html

https://kaminashi-developer.hatenablog.jp/entry/gormv1-v2