Go言語(Golang)でMySQLを使ったAPIを作成する

#まえがき
APIを作るにあたって大規模の同時接続に対応させたいと考え，
PythonのDjangoで作成していたAPIをGolangで作成し直すことにした。

なかなか思い通りに動かせず，つまづいた点が多くあったので記録に残す。

[PythonでGoのパフォーマンスに勝てるかを検証する](https://qiita.com/shimakaze_soft/items/8a493f230eabafe45209)




#Goの環境設定

[Goダウンロード](https://golang.org/)



```
// 確認
% go version
go version go1.16.2 darwin/amd64

// 環境情報
% go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/akidon/Library/Caches/go-build"
GOENV="/Users/akidon/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/akidon/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/akidon/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.16.2"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/akidon/program/go/goAPI/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/qg/6f_bf3fx4qs2gxwzhbc_pks00000gn/T/go-build311814080=/tmp/go-build -gno-record-gcc-switches -fno-common"

// PATH追加
% open ~/.zshrc
```
以下を追加

```a.zshrc
#Goのパス
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOROOT/bin:$PATH
export PATH=$GOPATH/bin:$PATH
```

## 動作確認

適当なフォルダにhello.goというファイルを作成して、Visual Studio Codeで下記のように編集。

```go
package main

import "fmt"

func main() {
    fmt.Println("Hello!");
}
```

ターミナルで先ほどのフォルダに移動して、 `go run hello.go` コマンドを実行。「Hello!」と表示されれば成功。

```
% go run hello.go
Hello!
```






#Goのパッケージ
Echo ... RestfullAPIの作成(他にもGinやGorillaなどがある)
Gorm ... MySQLを操作するため

もし以下のエラーが出れば
cannot find package "github.com/**hoge**"
go get github.com/**hoge** すればよい

```
% go mod init go-tools

% go get github.com/labstack/echo
% go get github.com/jinzhu/gorm
% go get github.com/joho/godotenv
```

# MySQLをダウンロード

[Mac 初期設定 備忘録](https://qiita.com/akidon0000/items/edae6807d0fe7448d56e)
からMySQLをダウンロード

```
% mysql.server start 
// mysql.server stop でサーバー停止

# MySQLに入る
% mysql -u root
```

```
# アカウント作成
mysql> create user 'ユーザー名'@'localhost' identified by 'パスワード';

# データベース作成('golang'というDBを作成)
mysql> create database golang;

# 作成したアカウントに権限を追加
mysql> GRANT ALL ON golang.* TO 'ユーザー名'@'localhost';

mysql> exit;
```

SequelAceを使うとMySQLの操作が楽
[SequelAceダウンロード](https://apps.apple.com/us/app/sequel-ace/id1518036000)



# GolangからMySQLのデータを追加・更新・取得

##MySQL側
テーブル名　users

```
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| golang             |  <- 作成
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

mysql> use golang;
mysql> show tables;
+------------------+
| Tables_in_golang |
+------------------+
| users            |
+------------------+

mysql> desc users;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int unsigned | NO   | PRI | NULL    | auto_increment |
| name        | varchar(255) | YES  |     | NULL    |                |
| age         | int          | YES  |     | NULL    |                |
| created_at  | timestamp    | YES  |     | NULL    |                | <- 必要
| updated_at  | timestamp    | YES  |     | NULL    |                | <- 必要
| deleted_at  | timestamp    | YES  |     | NULL    |                | <- 必要
+-------------+--------------+------+-----+---------+----------------+
```

![スクリーンショット 2021-04-04 13.15.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517291/7edb3977-6350-1bc5-154f-f95efaa3e046.png)

SequelAceを使ってこの様に作成した
Gormを使う際[created_at,updated_at,deleted_at]を作成しておく必要がある


##Golang側

gormを使用してMySQLを操作しようとしたが，つまずいた。
原因は詳しくは理解できていないが，命名が間違っていたっぽい。
大文字，小文字，複数，単数に注意して命名する必要があった。
MySQLではusersだが，Golangで呼び出すときは"User"
MySQLではnameだが，Golangで呼び出すときは"Name"
など注意が必要。

**確認動作チャックはしていないのでエラーが出る可能性があります**

```main.go
package routing

import (
	"fmt"
	"github.com/labstack/echo"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type User struct {
	gorm.Model
    //`json:"--"`はリクエストボディに指定する名前
	Name                string `json:"name"`
	Age                 int    `json:"age"`
}


func (u User) String() string {
	return fmt.Sprintf("Name:%s \n Age:%d \n ",
		u.Name,
		u.Age)
}

// ユーザーを登録，更新
func BaseAPI_user() echo.HandlerFunc {
	return func(c echo.Context) error {
		db := databases.GormConnect()
		defer db.Close()

		//リクエストボディ
		user := new(User)

		user1 := User{
			Name:     user.Name,
			Age:      user.Age,}

		// 追加
		insertUsers := []User{user1}
		insert(insertUsers, db)

		// 更新
		update(user1, db)

		// 検索（年齢一致）
		var count = search(user.Age, db)

		return c.JSON(200, count)
	}
}

func insert(users []User, db *gorm.DB) {
	for _, user := range users {
		db.NewRecord(user)
		db.Create(&user)
	}
}

func update(users User, db *gorm.DB) {
	var user User
	db.Model(&user).Where("id = ?", 1).Update(map[string]interface{}{"name": users.Name, "Age": users.Age})
}


func search(age int, db *gorm.DB) ([]User) {
	var user []User
	db.Raw("SELECT * FROM users WHERE age = ? ", age).Scan(&user)

	return user
}

// SQLConnect DB接続
func GormConnect() (database *gorm.DB) {
	// パスワード等を.envファイルから読み取る
	// program > go > .env
	err := godotenv.Load(fmt.Sprintf("../%s.env", os.Getenv("GO_ENV")))
	if err != nil {
		panic(err.Error())
	} else {
		fmt.Println("env読み取り成功")
	}

	DBMS := "mysql"                   // MySQL
	PROTOCOL := "tcp(localhost:3306)" // db:3306
	DBNAME := ""    // テーブル名
	USER := ""      // MySQLユーザー名
	PASS := ""  // パスワード

	CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
	db, err := gorm.Open(DBMS, CONNECT)
	if err != nil {
		panic(err.Error())
	} else {
		fmt.Println("DB接続成功")
	}
	return db
}
```