GORM で CRUD

# GORM について

GORM は Go 言語用の ORM です。

https://gorm.io/ja_JP/

# 検証用 DBテーブルの用意

Docker Compose 設定

```compose.yaml
services:
  postgres:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:
```

以下のコマンドでテーブルを作成します。

```sql
CREATE TABLE tasks(
    id uuid PRIMARY KEY,
    title varchar(100) NOT NULL
);
```

作成されたテーブル

```sql
postgres=# \d tasks;
                       Table "public.tasks"
 Column |          Type          | Collation | Nullable | Default 
--------+------------------------+-----------+----------+---------
 id     | uuid                   |           | not null | 
 title  | character varying(100) |           | not null | 
Indexes:
    "tasks_pkey" PRIMARY KEY, btree (id)
```

# インストール

以下のコマンドを使用してモジュールをインストールします。

[GORM のドキュメント](https://gorm.io/ja_JP/docs/index.html#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB) に従ってインストールします。

```shell
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
```

# GORM を使用した CRUD 操作

GORM を使用したシンプルな CRUD 操作を試してみます。

また、UUID の生成に以下を使用します。

https://github.com/google/uuid

## Create

```main.go
package main

import (
	"fmt"
	"os"

	"github.com/google/uuid"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Task struct {
	ID    uuid.UUID
	Title string
}

func main() {
	dsn := fmt.Sprintf("host=localhost user=postgres password=%s port=5432 sslmode=disable TimeZone=Asia/Tokyo", os.Getenv("POSTGRES_PASSWORD"))
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	task := &Task{
		ID:    uuid.New(),
		Title: "Test title",
	}

	db.Debug().Create(task)

	fmt.Printf("%+v", task)
}
```

```shell
go run ./main.go

2024/11/25 23:46:06 /Users/hirotowatanabe/workspace/tmp_20241125_gorm_for_blog/main.go:29
[8.060ms] [rows:1] INSERT INTO "tasks" ("id","title") VALUES ('f5be0266-86d9-48d8-8cdc-93868d767bb0','Test title')
&{ID:f5be0266-86d9-48d8-8cdc-93868d767bb0 Title:Test title}
```

```sql
postgres=# SELECT * FROM tasks WHERE id = 'f5be0266-86d9-48d8-8cdc-93868d767bb0';
                  id                  |   title    
--------------------------------------+------------
 f5be0266-86d9-48d8-8cdc-93868d767bb0 | Test title
(1 row)
```

## Read

```main.go
package main

import (
	"fmt"
	"os"

	"github.com/google/uuid"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Task struct {
	ID    uuid.UUID
	Title string
}

func main() {
	dsn := fmt.Sprintf("host=localhost user=postgres password=%s port=5432 sslmode=disable TimeZone=Asia/Tokyo", os.Getenv("POSTGRES_PASSWORD"))
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	var task Task
	db.First(&task, "id = ?", "586ccac4-6346-4ee2-a058-48633161c30e")

	fmt.Printf("%+v", task)
}
```

```shell
go run ./main.go
{ID:586ccac4-6346-4ee2-a058-48633161c30e Title:Test title}
```

## Update

```main.go
package main

import (
	"fmt"
	"os"

	"github.com/google/uuid"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Task struct {
	ID    uuid.UUID
	Title string
}

func main() {
	dsn := fmt.Sprintf("host=localhost user=postgres password=%s port=5432 sslmode=disable TimeZone=Asia/Tokyo", os.Getenv("POSTGRES_PASSWORD"))
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	var task Task
	db.First(&task, "id = ?", "f5be0266-86d9-48d8-8cdc-93868d767bb0")

	fmt.Printf("SELECTED: %+v", task)

	task.Title = "Updated test title"

	db.Debug().Save(task)

	fmt.Printf("UPDATED: %+v", task)
}
```

```shell
go run ./main.go
SELECTED: {ID:f5be0266-86d9-48d8-8cdc-93868d767bb0 Title:Test title}
2024/11/25 23:52:04 /Users/hirotowatanabe/workspace/tmp_20241125_gorm_for_blog/main.go:31
[4.305ms] [rows:1] UPDATE "tasks" SET "id"='f5be0266-86d9-48d8-8cdc-93868d767bb0',"title"='Updated test title' WHERE "id" = 'f5be0266-86d9-48d8-8cdc-93868d767bb0'
UPDATED: {ID:f5be0266-86d9-48d8-8cdc-93868d767bb0 Title:Updated test title}
```

```sql
postgres=# SELECT * FROM tasks WHERE id = 'f5be0266-86d9-48d8-8cdc-93868d767bb0';
                  id                  |       title        
--------------------------------------+--------------------
 f5be0266-86d9-48d8-8cdc-93868d767bb0 | Updated test title
(1 row)
```

## Delete

```main.go
package main

import (
	"fmt"
	"os"

	"github.com/google/uuid"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Task struct {
	ID    uuid.UUID
	Title string
}

func main() {
	dsn := fmt.Sprintf("host=localhost user=postgres password=%s port=5432 sslmode=disable TimeZone=Asia/Tokyo", os.Getenv("POSTGRES_PASSWORD"))
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	var task Task
	db.First(&task, "id = ?", "f5be0266-86d9-48d8-8cdc-93868d767bb0")

	fmt.Printf("SELECTED: %+v", task)

	db.Debug().Delete(task)
}
```

```shell
go run ./main.go
SELECTED: {ID:f5be0266-86d9-48d8-8cdc-93868d767bb0 Title:Updated test title}
2024/11/25 23:55:21 /Users/hirotowatanabe/workspace/tmp_20241125_gorm_for_blog/main.go:29
[4.327ms] [rows:1] DELETE FROM "tasks" WHERE "tasks"."id" = 'f5be0266-86d9-48d8-8cdc-93868d767bb0'
```

```sql
postgres=# SELECT * FROM tasks WHERE id = 'f5be0266-86d9-48d8-8cdc-93868d767bb0';
 id | title 
----+-------
(0 rows)
```