sql-migrateとGormでマイグレーションとシードを実装する
- 2020/12/06
- Go言語
Goのボイラープレートを実装していたときの備忘録として残しておく。
まず今回使用するパッケージについて
sql-migrate
sql-migrateはその名の通り、SQLをマイグレートするためのツールでGoで作られています。マイグレーションするためのパッケージはいくつか存在していますが、個人的にsql-migrateがシンプルで使いやすかったという理由で採用しています。
gorm
続いてgormはGo製のORMパッケージになります。言わずもがなこちらもポピュラーなパッケージになります。他にもSQLBoiler,xormといったパッケージが存在しているようですが、最もGithubのスター数が多いことを理由にGORMを使用します。
GORMにもモデル定義に合わせた自動マイグレーション機能が用意されていますが、不足しているカラムやインデックスの生成はするが、カラム削除まではやってくれないなど、使っていくには不十分な点がります。そのため一番使いやすかったsql-migrateを使用しています。
実装
では実装に入っていきます。
使用するDBはPostgresqlでも構いませんが自分が使い慣れたMySQL使用することにします。
ディレクトリ構成は以下
├── dbconfig.yml
├── Dockerfile
├── docker-compose.yml
└── db
├── migrations
├── initdb.d
│ └── create_db.sql
├── mysql_data
└── seeds
└── seed.go
まずはdockerで開発環境準備を。
docker-compose.yml
version: '3'
services:
app:
build: .
container_name: go_container
tty: true
volumes:
- ./:/app
working_dir: /app
ports:
- "8080:8080"
depends_on:
- db
db:
image: mysql:5.7
container_name: go_db
volumes:
- ./db/mysql_data:/var/lib/mysql
- ./db/initdb.d:/docker-entrypoint-initdb.d
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: root
MYSQL_PASSWORD: root
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
データベースを作成するためのSQL
db/initdb.d/create_db.sql
CREATE DATABASE IF NOT EXISTS api_db;
もしも立ち上げた時に初期実行ファイルが実行されない場合、永続化しているディレクトリ(mysql_data)を削除して再び立ち上げててください。永続化ディレクトリが既に存在している場合は初期実行の処理が行われません。
Dockerfile
FROM golang:latest
RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/jinzhu/gorm
RUN go get github.com/rubenv/sql-migrate/...
EXPOSE 8080
今回は使用するパッケージが少ないので、Dockerfileに使用するパッケージをそのまま書いています。本来はmodを使うと便利です。
では続いてsql-migrateをDBに接続するためdbconfig.ymlを作成します
dbconfig.yml
development:
dialect: mysql
datasource: root:root@tcp(go_db:3306)/api_db?charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=true
dir: db/migrations
table: migrations
もしもPostgresqlに繋ぎたい場合は以下のように書いてください
dbconfig.yml
development:
dialect: postgres
datasource: host=go_db user=root port=5432 password=root sslmode=disable
dir: db/migrations
table: migrations
これでした準備ができたのでコンテナに入って作業をします
$ docker-compose up -d
$ docker-compose exec app bash
sql-migrateで使用できるのは以下のコマンドになります
$ sql-migrate status // ステータス確認 実行されているmigrate fileを確認可能
$ sql-migrate new articles // migration file作成
$ sql-migrate up // migration を実行
$ sql-migrate down // migrationをrollback
以下を実行してarticleのマイグレーションファイルを作成します
$ sql-migrate new articles
db/migrationsにコメントが記載された空のファイルが作成されます。
そこにSQLを書いて行きます。
-- +migrate Up
CREATE TABLE IF NOT EXISTS articles (
id bigint AUTO_INCREMENT NOT NULL,
title VARCHAR(255),
body TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
-- +migrate Down
DROP TABLE IF EXISTS articles;
マイグレーション実行
$ sql-migrate up
次にシードファイルを作成するためのseed.goを書きます。
package main
import (
"fmt"
"log"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
// Article is
type Article struct {
Title string
Body string
CreatedAt time.Time
UpdatedAt time.Time
}
func seeds(db *gorm.DB) error {
for i := 0; i < 10; i++ {
article := Article{Title: "title", Body: "body", CreatedAt: time.Now(), UpdatedAt: time.Now()}
if err := db.Create(&article).Error; err != nil {
fmt.Printf("%+v", err)
}
}
return nil
}
func openConnection() *gorm.DB {
db, err := gorm.Open("mysql", "root:root@tcp(go_db:3306)/api_db?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
log.Fatalf("Couldn't establish database connection: %s", err)
}
return db
}
func main() {
db := openConnection()
defer db.Close()
if err := seeds(db); err != nil {
fmt.Printf("%+v", err)
return
}
}
これで実装できたので動かしてみましょう
$ go run seed.go
できたらmysqlの方にも入って確認してみましょう
$ docker-compose exec db bash
$ mysql -u root -p
$ use api_db;
$ select * from articles;
articlesのDBにもデータが入ってることを確認できたらOKです。