7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[golang]Gormでのモデル定義とオートマイグレーションでできるテーブル定義

Last updated at Posted at 2018-12-15

お題

Golangで有名なORマッパーライブラリのGormには、定義した構造体に応じたRDBのテーブルを自動生成する機能がある。
このオートマイグレーションをいろいろ試してみる。

開発環境

# OS

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"

# Golang

$ go version
go version go1.11.2 linux/amd64

バージョンの切り替えはgoenvで行っている。

# Gorm

Version: v1.9.2

実践

各試行共通の準備

RDBはMySQLを対象とする。
ローカルで↓のようなYamlを作っておく。
(dockerとdocker-composeは当然インストール済)

[docker-compose.yml]
version: '3'
services:
  db:
    image: mysql:5.7.24
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpass
      MYSQL_DATABASE: testdb

起動。

$ sudo docker-compose up

試しにコンテナ内のDBに接続。

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
831188d7e688        mysql:5.7.24        "docker-entrypoint.s…"   5 minutes ago       Up 5 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   gorm_db_1_46a022cef614
$
$ sudo docker exec -it gorm_db_1_46a022cef614 bash
root@831188d7e688:/# mysql -utestuser -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.24 MySQL Community Server (GPL)

 〜〜省略〜〜

mysql>

各試行共通のmain.go

[main.go]
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 db.Close()
	db.LogMode(true)
	if err := db.DB().Ping(); err != nil {
		panic(err)
	}
	db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8").AutoMigrate(
		&model.User{},
		&model.UserGroup{},
		// ★↑のオートマイグレーション対象モデルは各試行によって変わる★
	)
}

gorm.Modelを使ったカラム自動生成

以下のように中身は同じ2つの構造体を定義。

[model/user.go]
type User struct {
	gorm.Model
}
[model/usergroup.go]
type UserGroup struct {
	gorm.Model
}

マイグレート実行。

$ go run main.go

すると、↓のようなテーブル名は異なるが中身が同じテーブルが生成される。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| testdb             |
+--------------------+
2 rows in set (0.00 sec)

mysql> use testdb;
Database changed
mysql> show tables;
Empty set (0.00 sec)

mysql> 
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| user_groups      |
| users            |
+------------------+
2 rows in set (0.00 sec)

mysql> desc users;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
| deleted_at | timestamp        | YES  | MUL | NULL    |                |
+------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> desc user_groups;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
| deleted_at | timestamp        | YES  | MUL | NULL    |                |
+------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

明示的に定義しなくても、なんかいろいろカラム作ってくれてる。

■明示的にカラムの定義を指示

[model/hogefuga.go]
type HogeFuga struct {
	HogeID             int    `gorm:"primary_key"`           // プライマリーキーの指示
	Name               string `gorm:"column:hoge_fuga_name"` // プロパティとは別の名前でカラム定義
	Field              string `gorm:"type:varchar(100)"`     // 型・桁を指示
	UniqueField        string `gorm:"unique"`                // 重複不可を指示
	NotNullField       string `gorm:"not null"`              // Not Nullを指示
	IgnoreField        string `gorm:"-"`                     // カラム化しない指示
	AutoIncField       int    `gorm:"AUTO_INCREMENT"`        // オートインクリメント指示
	IndexField         int    `gorm:"index"`                 // インデックスを貼る
	UniqueIndexField   int    `gorm:"unique_index"`          // ユニークインデックスを貼る
	NamedIndexField    int    `gorm:"index:other_name_idx"`  // インデックスの名前を指示
	NoInstructionField string // 指定なし
}

こうなる。

mysql> desc hoge_fugas;
+----------------------+--------------+------+-----+---------+----------------+
| Field                | Type         | Null | Key | Default | Extra          |
+----------------------+--------------+------+-----+---------+----------------+
| hoge_id              | int(11)      | NO   | PRI | NULL    | auto_increment |
| hoge_fuga_name       | varchar(255) | YES  |     | NULL    |                |
| field                | varchar(100) | YES  |     | NULL    |                |
| unique_field         | varchar(255) | YES  | UNI | NULL    |                |
| not_null_field       | varchar(255) | NO   |     | NULL    |                |
| auto_inc_field       | int(11)      | YES  |     | NULL    |                |
| index_field          | int(11)      | YES  | MUL | NULL    |                |
| unique_index_field   | int(11)      | YES  | UNI | NULL    |                |
| named_index_field    | int(11)      | YES  | MUL | NULL    |                |
| no_instruction_field | varchar(255) | YES  |     | NULL    |                |
+----------------------+--------------+------+-----+---------+----------------+
10 rows in set (0.01 sec)

mysql> show index from hoge_fugas;
+------------+------------+-----------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table      | Non_unique | Key_name                          | Seq_in_index | Column_name        | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+-----------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hoge_fugas |          0 | PRIMARY                           |            1 | hoge_id            | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| hoge_fugas |          0 | unique_field                      |            1 | unique_field       | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| hoge_fugas |          0 | uix_hoge_fugas_unique_index_field |            1 | unique_index_field | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| hoge_fugas |          1 | idx_hoge_fugas_index_field        |            1 | index_field        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| hoge_fugas |          1 | other_name_idx                    |            1 | named_index_field  | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------+------------+-----------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.01 sec)

ユニークキー制約を確認。

mysql> insert into hoge_fugas(not_null_field, unique_field) values('one', '001');
Query OK, 1 row affected (0.01 sec)

mysql> insert into hoge_fugas(not_null_field, unique_field) values('one', '001');
ERROR 1062 (23000): Duplicate entry '001' for key 'unique_field'

■テーブル名を指示

[model/foo.go]
type Foo struct {
	gorm.Model
}

func (Foo) TableName() string {
	return "foo_alias"
}
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| foo_alias        |
+------------------+

mysql> desc foo_alias;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
| deleted_at | timestamp        | YES  | MUL | NULL    |                |
+------------+------------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

■初期化時のプロパティに応じてテーブル名を変える

[model/baa.go]
type Baa struct {
	gorm.Model
	Kind int
}

func (b Baa) TableName() string {
	if b.Kind == 1 {
		return "admin_baa"
	} else {
		return "user_baa"
	}
}

これを↓のようにオートマイグレーション時にプロパティを指示することでテーブル名を変える。

[main.go]
func main() {
	db, err := gorm.Open("mysql", "testuser:testpass@tcp(127.0.0.1:3306)/testdb?charset=utf8&parseTime=True&loc=Local")
   〜〜省略〜〜
	db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8").AutoMigrate(
		&model.Baa{},
		&model.Baa{Kind: 1},
	)
}

すると、条件に応じたテーブル名のテーブルができている。

mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| admin_baa        |
| user_baa         |
+------------------+

まとめ

ひとまず基礎中の基礎のような部分をピックアップ。
次回はリレーション関係にトライしよう。

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?