Grom入門

# Gormとは
Go言語で扱うORMライブラリのひとつ
Gormにはマイグレーション機能もついている

# 公式ドキュメント
ここを読み込めばだいたいわかる
http://gorm.io/ja_JP/docs/

# 導入方法
Go言語の機能のgo getコマンドを使ってインストールする

```
go get -u github.com/jinzhu/gorm
```

MySQLのドライバーをインストール

```
go get github.com/go-sql-driver/mysql
```

設定ファイルをtomlというファイルで設定するためインストール
※tomlである必要はないが今回はtomlで設定

```
go get -u github.com/BurntSushi/toml
```

#接続方法

config.tomlファイルを作成

```db.toml
[Database]
driver = "mysql"
server = ""
user = "hoge"
password = ""
database = "hoge"
charset = "utf8"
parseTime = "true"
```

db用のgoファイルを作成

```main.go
package main

import (
	"fmt"

	"github.com/BurntSushi/toml"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type DbConfig struct {
	Driver    string
	Server    string
	User      string
	Password  string
	Database  string
	Charset   string
	ParseTime string
}

type Config struct {
	Database DbConfig
}

func (d DbConfig) DSN() string {
	return fmt.Sprintf("%s:%s@%s/%s?charset=%s&parseTime=%s", d.User, d.Password, d.Server, d.Database, d.Charset, d.ParseTime)
}

func (c Config) Db() (string, string) {
	return c.Database.Driver, c.Database.DSN()
}

func GetConfig() Config {
	var config Config
	_, err := toml.DecodeFile("config.toml", &config)
	if err != nil {
		panic("unloaded config file")
	}

	return config
}
```

以下の処理で db に接続されたDBが代入される

```main.go
var db *gorm.DB

func init() {
	var err error
	config := GetConfig()

	db, err = gorm.Open(config.Db())
	if err != nil {
		panic(err.Error())
	}
}
```

#使い方

## 準備

go 言語にはクラスがないのでstructにモデルデータを記述
適当にAccountモデルを作成
go では 変数名 型 の順で宣言

```
type Account struct {
	ID		int
	Name	string
	Email	string
	Address	string
}
```

以下の関数を呼び出すとstructに定義された要素のテーブルを作成してくれる

```
db.AutoMigrate(&Product{})
```

### Tips

変数名は頭文字大文字のキャメルケースで
頭文字が大文字でないとAutoMigrateが機能しない
structに定義した名称の複数形で作成される（Account → Accounts）

カラムの削除などについては以下を参照
http://gorm.io/ja_JP/docs/migration.html

gorm.Modelをstructに加えると以下の要素を加えることができる
これによって論理削除を簡単に実装できる

```
type Model struct {
  ID        uint `gorm:"primary_key"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time
}
```

元のAccountがカラム数が4だったのに
Account（id, name, email, address）

```
type Account struct {
	ID    int
	Name   string
	Email   string
	Address   string
}
```

gorm.Modelを加えるとカラム数が7になる
Account（id, created_at, updated_at, deleted_at, name, email, address）

```
type Account struct {
	gorm.Model
	Name   string
	Email   string
	Address   string
}
```

参照方法は直下に変数があるように扱う

```
	account := Account{}
	db.First(&account)

	// gorm.Model
	fmt.Println(account.ID)
	fmt.Println(account.CreatedAt)
	fmt.Println(account.UpdatedAt)
	fmt.Println(account.DeletedAt)

	// 自作変数
	fmt.Println(account.Name)
	fmt.Println(account.Email)
	fmt.Println(account.Address)
```


詳しくは以下を参照
http://gorm.io/ja_JP/docs/conventions.html

## 操作（CRUD）
### Create

CreateとNewRecordの2つの関数がある

NewRecord
プライマリキーが重複していてもエラーにならない（処理は実行されない）

```
account := Account{Name: "Jinzhu", Address: "tokorozawa"}
db.NewRecord(account)
```

Create
参照を渡す、プライマリキーが重複しているとエラーになる

```
account := Account{Name: "Jinzhu", Address: "tokorozawa"}
db.Create(&account)
```

その他詳細は以下
http://gorm.io/ja_JP/docs/create.html

### Query

例を書くより公式の方がわかりやすいので
http://gorm.io/ja_JP/docs/query.html

#### Tips

全レコードの取得

```
db.Find([]&user) // 全レコード
```

DeletedAtを持っているstructでdeleted_atに値が入っている場合、勝手にselectから除外してくれる

実行クエリの確認

```
db.Debug().Where("id = ?", accountID).Find(&account)
```

dbの後にDebugをつける
Firstでは使えないみたい

### Update

例を書くより公式の方が〜
http://gorm.io/ja_JP/docs/update.html

### Delete

```
db.Delete(&email)
```

例を書くより〜
http://gorm.io/ja_JP/docs/delete.html

#### Tips

 プライマリキーが代入されていない場合、 **全てのレコード** が削除される 
DeletedAtを持っているstructでdeleted_atに値が入っている場合、勝手に物理削除ではなく論理削除（deleted_atの更新）にしてくれる

### まとめソース
```main.go
package main

import (
	"fmt"

	mydb "./db"
	"github.com/BurntSushi/toml"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type Account struct {
	gorm.Model
	Email   string
	Address string
	Name    string
}

var db *gorm.DB

func main() {
	// create
	fmt.Println("create")
	account := Account{Name: "Jinzhu", Address: "tokorozawa"}
	db.Create(&account)

	// query
	fmt.Println("")
	fmt.Println("query")
	account2 := Account{}
	db.First(&account2)
	fmt.Println(account2.ID)
	fmt.Println(account2.Name)
	fmt.Println(account2.CreatedAt)

	// update
	fmt.Println("")
	fmt.Println("update")
	account2.Address = "utsunomiya"
	db.Save(&account2)

	account3 := Account{}
	db.First(&account3)
	fmt.Println(account3.Address)

	// delete
	fmt.Println("")
	fmt.Println("delete")
	db.Delete(&account3)

	account4 := Account{}
	db.First(&account4)
	fmt.Println(account4.ID)
}

func init() {
	var err error
	config := mydb.GetConfig()

	db, err = gorm.Open(config.Db())
	if err != nil {
		panic(err.Error())
	}
	db.AutoMigrate(&Account{})

}

type DbConfig struct {
	Driver    string
	Server    string
	User      string
	Password  string
	Database  string
	Charset   string
	ParseTime string
}

type Config struct {
	Database DbConfig
}

func (d DbConfig) DSN() string {
	return fmt.Sprintf("%s:%s@%s/%s?charset=%s&parseTime=%s", d.User, d.Password, d.Server, d.Database, d.Charset, d.ParseTime)
}

func (c Config) Db() (string, string) {
	return c.Database.Driver, c.Database.DSN()
}

func GetConfig() Config {
	var config Config
	_, err := toml.DecodeFile("config.toml", &config)
	if err != nil {
		panic("unloaded config file")
	}

	return config
}
```

以下で実行
init → main の順で実行される

```
go run main.go
```

結果

```
create

query
3
Jinzhu
2018-12-17 07:17:25 +0000 UTC

update
utsunomiya

delete
0
```


#### 参考サイト様
[【GORM】Go言語でORM触ってみた](https://qiita.com/chan-p/items/cf3e007b82cc7fce2d81)
[gormで実行されているクエリ(SQL文)を確認する](https://kamykn.github.io/post/gorm%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%82%AF%E3%82%A8%E3%83%AAsql%E6%96%87%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B/)
[GORMの接続情報をtomlで設定できるようにした](https://polidog.jp/2016/08/09/golang_gorm1/)