GolangにORM導入(ent migration編)

#記事作成にあたり
GolangでORMを導入し、マイグレーション/CRUD処理を実施。GolangのORMはGORMやPrismaが有名だが、entを採用。entの説明や導入方法を記載。

#アジェンダ
1. entとは
2. 環境情報
3. インストール
4. スキーマ定義
5. マイグレーション実行 

#1. entとは
Facebook社(Meta)提供のGolangのORM。公式リファレンスや機能も充実。
![スクリーンショット 2022-02-14 13.03.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/4dc8b1f1-5baa-1af3-d3b9-2d1d0e4f950c.png)

#2. 環境情報
PC：Mac(CPUはintel製)
Go：1.17.6 ←Goのver大事。versionは1.16以降をインストールしてください。
開発エディタ：Visual Studio Code

#3. インストール
```shell:ディレクトリ作成
~/go/src $ mkdir ent_sample
~/go/src $ cd ent_sample 
※GOPATH配下にディレクトリは作成ください。
```
```shell:go.modの作成
~/go/src/ent_sample $ go mod init
go: creating new go.mod: module ent_sample
```

```shell:entのインストール
~/go/src/ent_sample $ go get -d entgo.io/ent/cmd/ent
```

#4. スキーマ定義
```shell:テンプレート作成(Userテーブル)
~/go/src/ent_sample $ ent init User
※生成スキーマはアッパーキャメルでないとエラー出ます。
```

```shell:テンプレート作成後のディレクトリ構成
~/go/src/ent_sample $ tree
.
├── ent
│   ├── generate.go
│   └── schema
│       └── user.go
├── go.mod
└── go.sum

2 directories, 4 files
```
→generate.goと /schema/user.goが作成されている。中身を見ていきましょう。

```go:generate.go
package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema

```

```go:/schema/user.go
package schema

import "entgo.io/ent"

// User holds the schema definition for the User entity.
type User struct {
	ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
	return nil
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return nil
}
```

usersテーブルのスキーマ設定のため、Fields関数を変更。

```go:/schema/user.go
package schema

import "entgo.io/ent"

// User holds the schema definition for the User entity.
type User struct {
	ent.Schema
}

// Fields of the User.　　←Fields関数を修正。
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.Int("age").
            Positive(),
        field.String("name").
            Default("unknown"),
    }
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return nil
}
```

```shell:ファイル生成
~/go/src/ent_sample $ go generate ./ent  
```

```shell:ファイル生成後のディレクトリ構成
~/go/src/ent_sample $ tree
.
├── ent
│   ├── client.go
│   ├── config.go
│   ├── context.go
│   ├── ent.go
│   ├── enttest
│   │   └── enttest.go
│   ├── generate.go
│   ├── hook
│   │   └── hook.go
│   ├── migrate
│   │   ├── migrate.go
│   │   └── schema.go
│   ├── mutation.go
│   ├── predicate
│   │   └── predicate.go
│   ├── runtime
│   │   └── runtime.go
│   ├── runtime.go
│   ├── schema
│   │   └── user.go
│   ├── tx.go
│   ├── user
│   │   ├── user.go
│   │   └── where.go
│   ├── user.go
│   ├── user_create.go
│   ├── user_delete.go
│   ├── user_query.go
│   └── user_update.go
├── go.mod
└── go.sum

8 directories, 24 files
```
→ファイルが多数作成されていることが確認されますね。

#5. マイグレーション実行
dockerでPostgreSQLを準備。

```docker:docker-compose.yml
version: '3.1'

services:
  postgres:
    container_name: postgres
    image: postgres:13
    ports:
      - 5432:5432
    volumes:
      - ./.data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
```

```shell:PostgreSQLインスタンス生成
~/go/src/ent-test $ docker-compose up -d
Creating network "ent-test_default" with the default driver
Creating postgres ... done
```

追加ライブラリインストールの上、main.goを作成

```shell:ライブラリの追加インストール(postgressのドライバー)
go get github.com/lib/pq
```

```go:main.go
package main

import (
	"context"
	"ent_sample/ent"
	"ent_sample/ent/migrate"
	"log"

	_ "github.com/lib/pq" //postgresは必須。https://github.com/bmizerany/pq.go/issues/15
)

func main() {
    client, err := ent.Open("postgres", "postgres://postgres:postgres@localhost:5432/enttest?sslmode=disable") // sslmodeが必要。https://stackoverflow.com/questions/21959148/ssl-is-not-enabled-on-the-server
    if err != nil {
        log.Fatalf("failed connecting to postgres: %v", err)
    }
    defer client.Close()
    ctx := context.Background()
    // マイグレーションの実行
    err = client.Schema.Create(
        ctx, 
        migrate.WithDropIndex(true),
        migrate.WithDropColumn(true), 
    )
    if err != nil {
        log.Fatalf("failed creating schema resources: %v", err)
    }
}

```

```shell:DBスキーマ作成
~/go/src/ent_sample $ psql -h localhost -p 5432 -U postgres 
Password for user postgres: 
psql (14.1, server 13.4 (Debian 13.4-1.pgdg100+1))
Type "help" for help.

postgres=# \l
                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges   
-----------+----------+----------+------------+------------+-----------------------
 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
(3 rows)

postgres=# create database ent_sample;
CREATE DATABASE
postgres=# \l
                                 List of databases
    Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges   
------------+----------+----------+------------+------------+-----------------------
 ent_sample | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
 postgres   | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
            |          |          |            |            | postgres=CTc/postgres
 template1  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
            |          |          |            |            | postgres=CTc/postgres
(4 rows)
```

```golang:main.goの実行
~/go/src/ent_sample $ go run main.go                                            
~/go/src/ent_sample $ 
```

```shell:環境確認
~/go/src/ent_sample $ psql -h localhost -p 5432 -U postgres 
Password for user postgres: 
psql (14.1, server 13.4 (Debian 13.4-1.pgdg100+1))
Type "help" for help.

postgres=# \c ent_sample 
psql (14.1, server 13.4 (Debian 13.4-1.pgdg100+1))
You are now connected to database "ent_sample" as user "postgres".
ent_sample=# \dt
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | users | table | postgres
(1 row)

ent_sample=# \d users
                                 Table "public.users"
 Column |       Type        | Collation | Nullable |             Default              
--------+-------------------+-----------+----------+----------------------------------
 id     | bigint            |           | not null | generated by default as identity
 age    | bigint            |           | not null | 
 name   | character varying |           | not null | 'unknown'::character varying
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
```


#参考文献
https://entgo.io/

https://github.com/ent