逆引きgorm

gormの使い方を自分向けにまとめました。適宜追加していきます。

基本的にこれがわかってたらなんとかなる。
そして大体[公式ドキュメント](http://doc.gorm.io/)に書いてあります！

以下のようなデータを用意して色々操作してみたいと思います。

```shell
# 初期状態
mysql> select * from persons;
+----+------+-----+-------+---------------------+---------------------+
| id | name | age | hobby | created_at          | updated_at          |
+----+------+-----+-------+---------------------+---------------------+
|  1 | A    |  20 | NULL  | 2018-10-04 11:13:35 | 2018-10-04 11:13:35 |
|  2 | B    |  30 | c     | 2018-10-04 11:13:35 | 2018-10-04 11:13:35 |
|  3 | C    |  15 | NULL  | 2018-10-04 11:13:35 | 2018-10-04 11:13:35 |
|  4 | D    |  44 | d     | 2018-10-04 11:13:35 | 2018-10-04 11:13:35 |
|  5 | E    |  60 | NULL  | 2018-10-04 11:13:35 | 2018-10-04 11:13:35 |
+----+------+-----+-------+---------------------+---------------------+
5 rows in set (0.00 sec)
```

```golang
// Go言語でのPeople定義
package main

import (
        "time"
)

type Person struct {
        Id        int `json:"id" gorm:"primary_key"`
        Name      string `json:"name"`
        Age       int `json:"age"`
        Hobby     string `json:"hobby"`
        CreatedAt time.Time `json:"createdAt" sql:"DEFAULT:current_timestamp"`
        UpdatedAt time.Time `json:"updatedAT" sql:"DEFAULT:current_timestamp on update current_timestamp"`
}

```

## 最初の1件を取得する
```golang
var person Person
db.First(&person)
fmt.Printf("%+v\n", person)

// 出力結果
//{Id:1 Name:A Age:20 Hobby: CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// 実行SQL
// SELECT * FROM `people`   ORDER BY `people`.`id` ASC LIMIT 1

```

## 文字列検索をする
```golang
var people []Person
db.Where("name = ?", "B").Find(&people)
fmt.Printf("%+v\n", people)

// 出力結果
// [{Id:2 Name:B Age:30 Hobby:c CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}]
// 実行SQL
//  SELECT * FROM `people`  WHERE (name = 'B')
```

## 並び替えをする
```golang
var people []Person
db.Order("age desc").Find(&people)
fmt.Printf("%+v\n", people)

// 出力結果
// [{Id:5 Name:E Age:60 Hobby: CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:4 Name:D Age:44 Hobby:d CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:2 Name:B Age:30 Hobby:c CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:1 Name:A Age:20 Hobby: CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:3 Name:C Age:15 Hobby: CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}]
// 実行SQL
//  SELECT * FROM `people`   ORDER BY age desc

```

## 取得件数を絞り込む
```golang
var people []Person
db.Order("age desc").Limit(3).Offset(1).Find(&people)
fmt.Printf("%+v\n", people)

// 実行結果
// [{Id:4 Name:D Age:44 Hobby:d CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:2 Name:B Age:30 Hobby:c CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}
// {Id:1 Name:A Age:20 Hobby: CreatedAt:2018-10-04 11:37:57 +0000 UTC UpdatedAt:2018-10-04 11:37:57 +0000 UTC}]
// 実行SQL
// SELECT * FROM `people`   ORDER BY age desc LIMIT 3 OFFSET 1
```

## データを登録する
```golang
f := Person{
         Name: "F",
         Age: 12,
     }
db.Create(&f)
fmt.Printf("%+v\n", f)

```
このとき、hobbyはなにも設定していなかったですが実際DBには**空**が入ります。  

```sql
mysql> select id from people where id = 6 and hobby is null;
Empty set (0.00 sec)

mysql> select id from people where id = 6 and hobby = "";
+----+
| id |
+----+
|  6 |
+----+
1 row in set (0.00 sec)
```

NULLを入れるには、対象のカラムの型を**ポインタ**にします。

```golang
type Person struct {
        ~~~
        Hobby     *string `json:"hobby"`
        ~~~
}

```

最後上記の処理を実行すると

```sql
mysql> select id from people where id = 7 and hobby is null ;
+----+
| id |
+----+
|  7 |
+----+
1 row in set (0.00 sec)
```

NULLが入っているのがわかります。

## データを更新する
```golang
person := Person{ Id: 1 }
db.Model(&person).Update("hobby", "a")
fmt.Printf("%+v\n", person)

// 実行結果
// {Id:1 Name: Age:0 Hobby:a CreatedAt:0001-01-01 00:00:00 +0000 UTC UpdatedAt:2018-10-04 21:26:56.983776665 +0900 JST m=+0.003486322}
// 実行SQL
// UPDATE `people` SET `hobby` = 'a', `updated_at` = '2018-10-04 21:26:56'  WHERE `people`.`id` = '1'
```

どうやらgormはUpdateのときはupdated_atに自動で値を入れてくれるみたいです。  

```sql:実行後のレコード
mysql> select * from people where id = 1;
+----+------+-----+-------+---------------------+---------------------+
| id | name | age | hobby | created_at          | updated_at          |
+----+------+-----+-------+---------------------+---------------------+
|  1 | A    |  20 | a     | 2018-10-04 12:26:45 | 2018-10-04 12:26:57 |
+----+------+-----+-------+---------------------+---------------------+
1 row in set (0.00 sec)
```

nameやageはなにも設定してませんが、空になったりしませんでした。  
意図して空にしてみたらどうなるんでしょうか？

```golang
person := Person{ Id: 1, Age: 0 }
db.Model(&person).Update(person)
fmt.Printf("%+v\n", person)

// 実行結果
// {Id:1 Name: Age:0 Hobby:<nil> CreatedAt:0001-01-01 00:00:00 +0000 UTC UpdatedAt:2018-10-04 23:40:35.479965986 +0900 JST m=+0.003397484}
// 実行SQL
// UPDATE `people` SET `id` = '1', `updated_at` = '2018-10-04 23:40:35'  WHERE `people`.`id` = '1'
```

うーん、このやり方だとうまく動きません。
structでのintの初期値は0であるため、gormが検知できないんですね。

```golang
person := Person{ Id: 1 }
data := map[string]interface{} {
            "age": 0,
        }
db.Model(&person).Update(data)
fmt.Printf("%+v\n", person)

// 実行結果
// {Id:1 Name: Age:0 Hobby:<nil> CreatedAt:0001-01-01 00:00:00 +0000 UTC UpdatedAt:2018-10-04 23:46:12.378224923 +0900 JST m=+0.003037641}
// 実行SQL
// UPDATE `people` SET `age` = '0', `updated_at` = '2018-10-04 23:46:12'  WHERE `people`.`id` = '1'

```

更新の際はStructを使うのではなく、mapを使うと意図した動きになりました。

## データを削除する
```golang
person := Person{ Id: 5 }
db.Delete(&person)
// 実行SQL
// DELETE FROM `people`  WHERE `people`.`id` = '5'
```