gormでMigration＋Seedingを実行する

# はじめに
Qiita初投稿です。

Go言語のORMツールであるGORMでマイグレーションとシーディングを実装したところ、何点か詰まったところがあったので、記事にしてみようと思いました。

# GitHub
今回のソースコードはGitHub上にあるので、詳細はそちらを参考にしてください。
https://github.com/yasu2122yasu/article/tree/main/gorm-migration-seeding

# 前提条件
- M2 Macbook Air
- GoとDBのコンテナを使用
- Goのバージョンは1.18
- GoのフレームワークであるGinを利用（今回の実装はmain.goのみでも可能です）
- DBのイメージはMysql:8.0を使用

# doceker-compose
```yml:docker-compose.yml
version: '3.8'

services:
  api:
    container_name: api
    build:
      context: ./api
      dockerfile: Dockerfile
    tty: true
    ports:
      - 8080:8080
    env_file:
      - ./db/.env
    depends_on:
      - db
    volumes:
      - type: bind
        source: ./api/
        target: /go/src

  db:
    container_name: db
    build:
      context: ./db
      dockerfile: Dockerfile
    tty: true
    platform: linux/x86_64
    env_file:
      - ./db/.env
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: test_database
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password
    ports:
      - 3306:3306
    volumes:
      - ./db/initsql:/docker-entrypoint-initdb.d

volumes:
  db-store:
```

Goを動かすapiコンテナとMySQLコンテナをdocker-composeにより立ち上げています。

initsqlディレクトリ配下にSQLファイルを置くことで、初期のDB/テーブル作成ができるのですが、自分の環境で実行するとデータベースを変更できなくなったので今回はアプリケーション側でmigrationとseedingを実行したいと思います。

```
volumes:
      - ./db/initsql:/docker-entrypoint-initdb.d
```

# Migraiton
Migraitonはデータベースに接続した後にすぐ実行するようにしています。

```go:database.go
package database

import (
	"api/model"
	"fmt"
	"os"

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

var Db *gorm.DB

func ConnectDatabase() {
	dsn := fmt.Sprintf("%s:%s@tcp(db:3306)/%s?charset=utf8&parseTime=true", os.Getenv("MYSQL_USER"), os.Getenv("MYSQL_PASSWORD"), os.Getenv("MYSQL_DATABASE"))
	var err error
	Db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("Failed to connect to database")
	}

	Db.AutoMigrate(&model.User{})

	UserSeed(Db)
}
```

以下の部分でマイグレーションを実行するmodelを指定しています。
```
Db.AutoMigrate(&model.User{})
```
今回使用するUserモデルは次のようになります。
```go:User.go
package model

import (
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name     string
	Email    string
	Password string
}
```
gorm.Modelには、ID、CreatedAt、UpdatedAt、DeletedAtがあらかじめ用意されているので、idなどの宣言は不要です。ただし、これらのフィールドが不要な場合は別途gormタグを利用するので、少し不便を感じるかもしれません。

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

# Seeding
main.go実行時に、seedingも実行するため、usersテーブルのレコードをカウントして、レコードがあればseedingを実行しないようにしています。

```go:seeder.go
package database

import (
	"api/model"
	"fmt"

	"gorm.io/gorm"
)

func UserSeed(db *gorm.DB) error {
	var count int64

	// main.goが実行される度にレコードが生成されないようにする。
	db.Model(&model.User{}).Count(&count)
	if count > 0 {
		return nil
	}
	users := model.User{Name: "Yamada Taro", Email: "mailmail@gmail.com", Password: "password"}

	if err := db.Create(&users).Error; err != nil {
		fmt.Printf("%+v", err)
	}

	return nil
}
```
# 実行
```
¥ docker-compose up -d
```
でコンテナを起動してください。

スクショになりますが、実行結果を貼っておきます。
　![スクリーンショット 2023-12-10 23.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562304/f10b476f-0706-cbe9-e2a4-190cb12d2978.png)


# おわりに
今回はgormでマイグレーション・シーディングを実行してみました。

ただ現在会社で関わっているプロジェクトではentに興味があるので、entでのMigration・Seedingも実装してみたいなと思いました。

https://github.com/ent/ent

# 参考文献
https://qiita.com/2san/items/dd2c6d0449262a42b728