Gormの使い方についてまとめてみた

goを使ってGormを使ってデータベース操作する必要が出てきたのでいろいろ調べた結果をまとめます。


# データベーススキーマの作成

まずはスキーマの作成の仕方についてみていきます。

### Migrate

Migrateは、接続先のスキーマを参照して、テーブルやカラムがなければ自動的に作ってくれるコマンドです。
足りない関連やテーブルを自動で作ったりはしてくれるが、勝手にカラムを消したりすることはしないとのこと。

> **Auto Migration**
> 
> Automatically migrate your schema, to keep your schema up to date.
> 
> > NOTE: AutoMigrate will create tables, missing foreign keys, constraints, columns and indexes. > > It will change existing column’s type if its size, precision, nullable changed. It WON’T delete > unused columns to protect your data.
> > 

```
db.AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
 // Add table suffix when creating tables
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
```

DBの関連を貼りたくない場合は、以下の様に書くことで自動的に関連が追加されることを抑止できる。

> NOTE AutoMigrate creates database foreign key constraints automatically, you can disable this feature during initialization, for example:
> 

```
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  DisableForeignKeyConstraintWhenMigrating: true,
})
```

### 関連の貼り方

```go
type CreditCard struct {
		gorm.Model
		Number string
		UserID uint
	}

	type User struct {
		gorm.Model
		Name        string
		CreditCards []CreditCard
	}
```

Migrateコマンドは、一度に複数テーブル作成することもできるし、複数回呼び出すこともできるし。複数呼び出す場合、そのテーブル間で関連がある場合でも関連が作成されないので注意したい。以下に具体例を示す。

これだと関連が作られない

```jsx
	db.AutoMigrate(&CreditCard{})
	db.AutoMigrate(&User{})
```

これだと関連が作られる

```jsx
	db.AutoMigrate(&CreditCard{}, &User{})
```

# データの登録方法

### データの登録

以下のような形でMapを直接指定して登録することが可能。

```
db.Model(&Product{}).Create(map[string]interface{}{
		"Code": "jinzhu", "Price": 18,
	})
```

存在しない項目が指定された場合は以下のようなエラーが出力されて登録できない

> 2022/10/01 16:02:21 /Users/Documents/git-repo/first-golang/gorm_try/main.go:70 Error 1054: Unknown column 'Codec' in 'field list’
> 

# データの検索方法

最後に登録したデータの検索の仕方を見ていきます。

### データの取得

データの取得は、処理実行の戻り値に返ってくるのではなく、まず変数を作って処理を実行するとそのアドレスにDBからの値が設定されるという仕組み。

Structで検索

この場合、0とか空文字の検索はできない。このような検索をしたいときはStructを使う。

```
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";

```

---

### mapで検索

To include zero values in the query conditions, you can use a map, which will include all key-values as query conditions, for example:

```
db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
```

---


こんな感じで変数に一旦入れて登録もできる

```go
user := User{
  Name:            "jinzhu",
  BillingAddress:  Address{Address1: "Billing Address - Address 1"},
  ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  Emails:          []Email{
    {Email: "jinzhu@example.com"},
    {Email: "jinzhu-2@example.com"},
  },
  Languages:       []Language{
    {Name: "ZH"},
    {Name: "EN"},
  },
}

db.Create(&user)
```

**関連する値の検索、取得**

関連先の項目の条件に合致するデータを取得する方法
関連先の情報を取得する方法
自分で書いていく必要がありそう？

[https://gorm.io/docs/query.html#Joins](https://gorm.io/docs/query.html#Joins)

**RawSqlで取得**

複雑なデータを取ってくる場合だと、どうしてもやっぱりスケール直接つなげて取得をするしかないケースもある。そういった時にどうやって取得をするかと言う0をいかに上げる。

```go
type Result struct {
		ID   int
		Name string
		Age  int
	}
	// raw SQLで検索
	var results []Result
	db.Raw("SELECT id, name, age FROM users WHERE age > ?", 20).Scan(&results)

	for index, result := range results {
		fmt.Println("🐵", index, result.ID, result.Name)
	}
```

以下の様に、mapを使って変数を渡すこともできる

```go
db.Raw("SELECT * FROM users WHERE (name = @name AND age > @age)",
		map[string]interface{}{"name": "test", "age": 11}).Find(&results)
```

**削除したらどうなる？**

Gormでは削除はSoftDeleteが基本となっている様で、以下の様に削除するとdeletedAtに削除した時刻が入ります。
削除フラグだと、データを消して再度同じデータを登録しようとしたときにキー重複エラーを起こしてしまいがちだけど、この方法ならそれも避けられる・・賢い。

```go
// UpSeart（なんで？）
	lang1 := Language{
		Code: "FN",
		Name: "フランス"}
	DB.Create(&lang1)
	fmt.Println("Language削除します")
	DB.Delete(&lang1)
```

こうやって書くと、対象のエンティティのデータが削除される前にHook処理を用意しておくこともできる
ここでACL処理を書けば権限制御も簡単・・・？

```go
func (u *Language) BeforeDelete(tx *gorm.DB) (err error) {
	fmt.Print("🌟🌟🌟RoleCheck🌟🌟🌟", u)
	//   if u.Role == "admin" {
	//     return errors.New("admin user not allowed to delete")
	//   }
	return
}
```

参考情報：
[https://gorm.io/docs/query.html](https://gorm.io/docs/query.html)

## まとめ

Gorm使ったデータの登録変更削除のやり方を見てきました。
これまでにJavaのHibernate,JavascriptのPrisma、Knexなどを利用したことがありますが、
それらと比べて多少独特な部分や自分で書かなくてはならない部分があるものの、これは見方を変えれば自分でカスタマイズできるというところでもあると思います。最低限必要な機能だけが用意されており、あとは用途に応じて自分で定義していける、とても使いやすいライブラリだと感じました