LoginSignup
12
11

More than 5 years have passed since last update.

[golang]gormを使ってローカル環境のMySQLとCRUD操作

Posted at

go言語を勉強してだいたい一週間経ちました。最初の3、4日間は書籍を見ながら文法の勉強をしていましたが、今はドキュメントとパッケージのドキュメントやGitHub、Qiitaを見ながら実践してます。

概要

今回はローカル環境にインストールしているMySQLとの接続とCRUD操作を実践してみます。

環境

  • go1.11.4 darwin/amd64
  • mysql Ver 8.0.12
  • curl 7.54.0

参考にしたもの

この記事でやること

  1. パッケージのインストール
  2. MySQLの権限付きユーザの生成
  3. goプログラム作成
    • RESTfullなAPIサーバ
    • CRUD操作
    • MySQLとの接続
  4. サーバーの起動
  5. curlでの動作確認

パッケージのインストール

まずはMySQLドライバーと、GoのORMのパッケージインストール

go-json-restがない方はこちらもインストール


$ go get github.com/go-sql-driver/mysql

$ go get github.com/jinzhu/gorm

$ go get github.com/ant0ine/go-json-rest/rest

MySQLの権限付きユーザの生成

$ mysql -u root -p

MySQL内で以下の設定を行う。Host以外、ユーザ名とパスワードはそれぞれ任意でお願いします。

-- データベース名:country
CREATE DATABASE country;

-- ユーザ名:gorm パスワード:passwordで作成。 Hostは'localhost'で設定する。
CREATE USER 'gorm'@'localhost' IDENTIFIED BY 'password';

-- ユーザ名:gorm に DB:country以下のテーブルも含めて権限を与える。
GRANT ALL PRIVILEGES ON country.* TO 'gorm'@'localhost';

権限が委譲できているか確認してみましょう。


SHOW GRANTS FOR 'gorm'@'localhost';

+-----------------------------------------------------------+
| Grants for gorm@localhost                                 |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `gorm`@`localhost`                  |
| GRANT ALL PRIVILEGES ON `country`.* TO `gorm`@`localhost` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)

goプログラムの作成

main.go
package main

import (
    "log"
    "net/http"
    "time"

    "github.com/ant0ine/go-json-rest/rest"
    _ "github.com/go-sql-driver/mysql" // エイリアスでprefixを省略できる
    "github.com/jinzhu/gorm"
)

type Country struct {
    Id        int64     `json:"id"`
    Name      string    `sql:"size:1024" json:"name"`
    CreatedAt time.Time `json:"createdAt"`
}

type Impl struct {
    DB *gorm.DB
}

func (i *Impl) InitDB() {
    var err error
    // MySQLとの接続。ユーザ名:gorm パスワード:password DB名:country
    i.DB, err = gorm.Open("mysql", "gorm:password@/country?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        log.Fatalf("Got error when connect database, the error is '%v'", err)
    }
    i.DB.LogMode(true)
}

// DBマイグレーション
func (i *Impl) InitSchema() {
    i.DB.AutoMigrate(&Country{})
}

func main() {

    i := Impl{}
    i.InitDB()
    i.InitSchema()

    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    router, err := rest.MakeRouter(
        rest.Get("/countries", i.GetAllCountries),
        rest.Post("/countries", i.PostCountry),
        rest.Get("/countries/:id", i.GetCountry),
        rest.Put("/countries/:id", i.PutCountry),
        rest.Delete("/countries/:id", i.DeleteCountry),
    )
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("server started.")
    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

// countriesテーブル内のデータ全出力
func (i *Impl) GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
    countries := []Country{}
    i.DB.Find(&countries)
    w.WriteJson(&countries)
}

// パスパラメータ:idの国の該当データを出力
func (i *Impl) GetCountry(w rest.ResponseWriter, r *rest.Request) {
    id := r.PathParam("id")
    country := Country{}
    if i.DB.Find(&country, id).Error != nil {
        rest.NotFound(w, r)
        return
    }
    w.WriteJson(&country)
}

// json形式のデータをPOST {name:国名}
func (i *Impl) PostCountry(w rest.ResponseWriter, r *rest.Request) {
    country := Country{}
    err := r.DecodeJsonPayload(&country)
    if err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
    }
    err = i.DB.Save(&country).Error
    if err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    w.WriteJson(&country)
}

// パスパラメータ:idの国の該当データのNameを変更し出力
func (i *Impl) PutCountry(w rest.ResponseWriter, r *rest.Request) {

    id := r.PathParam("id")
    country := Country{}
    if i.DB.First(&country, id).Error != nil {
        rest.NotFound(w, r)
        return
    }
    updated := Country{}
    if err := r.DecodeJsonPayload(&updated); err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    country.Name = updated.Name

    if err := i.DB.Save(&country).Error; err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    w.WriteJson(&country)
}

// パスパラメータ:idの国の該当データを削除
func (i *Impl) DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
    id := r.PathParam("id")
    country := Country{}
    if i.DB.First(&country, id).Error != nil {
        rest.NotFound(w, r)
        return
    }
    if err := i.DB.Delete(&country).Error; err != nil {
        rest.Error(w, err.Error(), http.StatusInsufficientStorage)
        return
    }
    w.WriteHeader(http.StatusOK)
}

サーバの起動

ターミナルでサーバを起動させます。

go run main.go

以下の内容が出力されていればサーバが起動しています。
解除したい場合は[Ctr] + [c]

[2019-01-25 15:56:32]  [93.30ms]  CREATE TABLE `countries` (`id` bigint AUTO_INCREMENT,`name` varchar(1024),`created_at` timestamp NULL , PRIMARY KEY (`id`))
[0 rows affected or returned ]
2019/01/25 15:56:32 server started.

マイグレーションは構造体内を分析してTable作られてるみたい。
サーバの再起動時、MySQL内にCountriesテーブルがあれば再度マイグレーションされることはありません。便利ですね〜。

curlでの動作確認

APIサーバの起動ができたら、別のターミナルを起動しcurlで動作確認をしてみましょう。

投稿

$ curl -i -H 'Content-Type:application/json' -d '{"name":"JAPAN"}' http://localhost:8080/countries

ステータスコード200とレスポンスが返ってきたら成功です。

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Fri, 25 Jan 2019 05:43:05 GMT
Content-Length: 86

{
  "id": 1,
  "country": "JAPAN",
  "createdAt": "2019-01-25T14:43:05.110759+09:00"
}

以下のコマンド、色々試してみてください。

全表示(index)

curl -i http://localhost:8080/countries

編集

curl -i -X PUT -H 'Content-Type:application/json' -d '{"name":"Japan"}' http://localhost:8080/countries/1

削除

curl -i -X DELETE http://localhost:8080/countries/2

感想

フレームワーク(Laravel)を経験しているので、ルーターやコントローラーの動きが理解しやすい。
また、モデルは構造体に相当している。ORMもLaravelと同じ。マイグレーションも便利ですね〜。

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11