Gormのオートマイグレーション注意点

# お題
GolangでORマッパーを探した時に、わりとすぐにヒットするのが[Gorm](https://github.com/jinzhu/gorm)かなと思う。
スター数も１万超え。
[公式ドキュメント](http://doc.gorm.io)も用意され、[日本語のドキュメント](http://gorm.io/ja_JP/docs/)もある。
ORマッパーとしての機能だけでなく、SQLビルディング、トランザクション制御、リレーション管理、オートマイグレーション、(実行したSQL文を出力する)ロガー等、多くの機能を備えている。
とても便利なのだけど、直感的に使えるがために、ちゃんとドキュメント読まずに使う（いや、読まないのはありえないこととはわかっているのだけど、つい）と当然のようにハマる。

これはドキュメントにはっきり[書いてある](http://doc.gorm.io/database.html#migration)ので注意点というかドキュメントちゃんと読めば気づくはずなのだけど、『オートマイグレーション』に関しては以下の仕様になっている。
『テーブル、カラムの新規追加は適用されるが、変更や削除は適用されない』
これは（既に登録されている）データの保護のためとのこと。
構造体（例：`type User struct{}`）を定義して `db.AutoMigrate(&User{})` とやるとそれだけで接続しているデータベースにテーブルが作成されるのでとても楽ではあるのだけど、何か定義の変更があったら構造体を修正すればそれだけでOKと思うと「おやっ？」となるので注意。

ちなみに、リレーション管理なんかもちょっと「？」と思うところがあるのだけど、それは以前↓で少し書いたので今回は触れず。
[[golang]Gormでのモデル定義とオートマイグレーションでできるテーブル定義（リレーション編）](https://qiita.com/sky0621/items/c4e98ef735e9f735bd1b)

# 開発環境
### # OS
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
```

### # Golang
```
$ go version
go version go1.11.4 linux/amd64
```

# 実践
お題に書いた通りなので実践も何もないのだけど、一応、動作を確認してみる。
ソースの全量は↓
https://github.com/sky0621/tips-go/tree/master/tools/gorm/part05_migration

## DB接続ソース

```go:[main.go]
package main

import (
	"part05_migration/model"

	"github.com/jinzhu/gorm"

	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := gorm.Open("mysql", "testuser:testpass@tcp(127.0.0.1:3306)/testdb?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := db.Close(); err != nil {
			panic(err)
		}
	}()
	db.LogMode(true)
	if err := db.DB().Ping(); err != nil {
		panic(err)
	}

	db.AutoMigrate(
		&model.User{},
	)
}
```

## ■１■　ユーザーテーブルを作成

### 構造体の定義

```go:[model/user.go]
package model

type User struct {
	ID   string `gorm:"primary_key"`
	Name string
}
```

### オートマイグレーション実行（テーブル作成）

```
$ go run main.go 

(/home/sky0621/work/src/go111/src/github.com/sky0621/tips-go/tools/gorm/part05_migration/main.go:25) 
[2019-04-22 09:05:54]  [55.66ms]  CREATE TABLE `users` (`id` varchar(255),`name` varchar(255) , PRIMARY KEY (`id`))  
[0 rows affected or returned ] 
```

```
mysql> desc users;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | varchar(255) | NO   | PRI | NULL    |       |
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
```

## ■２■　ユーザーテーブルのカラム追加と組織テーブル追加

### 構造体の定義

```go:[model/user.go]
package model

type User struct {
	ID       string `gorm:"primary_key"`
	Name     string
	Birthday int
}
```

```go:[model/organization.go]
package model

type Organization struct {
	ID   string `gorm:"primary_key"`
	Name string
}
```

### オートマイグレーション実行（カラム追加）

```
$ go run main.go 

(/home/sky0621/work/src/go111/src/github.com/sky0621/tips-go/tools/gorm/part05_migration/main.go:25) 
[2019-04-22 09:12:11]  [96.82ms]  ALTER TABLE `users` ADD `birthday` int;  
[0 rows affected or returned ] 
```

```
mysql> desc users;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id       | varchar(255) | NO   | PRI | NULL    |       |
| name     | varchar(255) | YES  |     | NULL    |       |
| birthday | int(11)      | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
```

### オートマイグレーション実行（テーブル追加）

オートマイグレーション対象の構造体を追加

```
$ git diff main.go
diff --git a/tools/gorm/part05_migration/main.go b/tools/gorm/part05_migration/main.go
index 257e20a..311f245 100644
--- a/tools/gorm/part05_migration/main.go
+++ b/tools/gorm/part05_migration/main.go
@@ -24,5 +24,6 @@ func main() {
        }
        db.AutoMigrate(
                &model.User{},
+               &model.Organization{},
        )
 }
```

```
$ go run main.go 

(/home/sky0621/work/src/go111/src/github.com/sky0621/tips-go/tools/gorm/part05_migration/main.go:25) 
[2019-04-22 09:16:09]  [53.84ms]  CREATE TABLE `organizations` (`id` varchar(255),`name` varchar(255) , PRIMARY KEY (`id`))  
[0 rows affected or returned ] 
```

```
mysql> desc organizations;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | varchar(255) | NO   | PRI | NULL    |       |
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
```

## ■３■　ユーザーテーブルのカラム変更・削除と組織テーブル削除

### 構造体の定義

```go:[model/user.go]
$ git diff model/user.go
diff --git a/tools/gorm/part05_migration/model/user.go b/tools/gorm/part05_migration/model/user.go
index 17e828b..2894a88 100644
--- a/tools/gorm/part05_migration/model/user.go
+++ b/tools/gorm/part05_migration/model/user.go
@@ -1,7 +1,6 @@
 package model
 
 type User struct {
-       ID       string `gorm:"primary_key"`
-       Name     string
-       Birthday int
+       ID   string `gorm:"primary_key"`
+       Name string `gorm:"column:name;type:varchar(64)"`
 }
```

オートマイグレーション対象の構造体を削除

```go:[main.go]
$ git diff main.go
diff --git a/tools/gorm/part05_migration/main.go b/tools/gorm/part05_migration/main.go
index 311f245..257e20a 100644
--- a/tools/gorm/part05_migration/main.go
+++ b/tools/gorm/part05_migration/main.go
@@ -24,6 +24,5 @@ func main() {
        }
        db.AutoMigrate(
                &model.User{},
-               &model.Organization{},
        )
 }
```

### オートマイグレーション実行（ユーザーテーブルのカラム変更・削除と組織テーブル削除）

```
$ go run main.go
$
```

```
mysql> desc users;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id       | varchar(255) | NO   | PRI | NULL    |       |
| name     | varchar(255) | YES  |     | NULL    |       |
| birthday | int(11)      | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> desc organizations;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | varchar(255) | NO   | PRI | NULL    |       |
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
```

ユーザーテーブル、組織テーブルともに定義に変更がない。
（ともにレコードは未投入だったので、レコード有無を問わず、テーブル構造の変更や削除は適用されない様子。）

# まとめ
ドキュメント記載通り、テーブル、カラムの新規追加はオートマイグレーション機能の適用対象になるが、変更、削除は適用対象外になっている。