Golang + GORM + MySQL でデータをやりとりする

環境

  • CentOS7
  • go1.x

GORM

The fantastic ORM library for Golang, aims to be developer friendly.
Go 言語で MySQL などのデータベースを簡単に扱える ORM マッパー。

MySQL を使ったサンプルを書いてみる。

インストール

※ dep は非推奨

$ cd /path/to/your/project
$ dep ensure -add github.com/jinzhu/gorm
$ dep ensure -add github.com/go-sql-driver/mysql

ファイル構成

  • path/to/go/src/psychedelicnekopunch/go-sample/
infrastructure/
  ├ Config.go
  └ DB.go
main.go

データベース準備

  • ここでは「test」という DB を作成して使用する

test.users テーブル

CREATE TABLE `users` (
  `id` int(10) UNSIGNED NOT NULL,
  `name` varchar(100) NOT NULL,
  `age` int(10) UNSIGNED NOT NULL,
  `is_student` tinyint(1) UNSIGNED NOT NULL,
  `created_at` int(10) UNSIGNED NOT NULL
);

接続〜各種サンプル (SQL文直接書く、参照、作成、更新)

infrastructure/Config.go

package infrastructure


type Config struct {
	DB struct {
		Production struct {
			Host string
			Username string
			Password string
			DBName string
		}
	}
}


func NewConfig() *Config {
	c := new(Config)
	c.DB.Production.Host = "localhost"
	c.DB.Production.Username = "username"
	c.DB.Production.Password = "password"
	c.DB.Production.DBName = "test"
	return c
}

infrastructure/DB.go

package infrastructure

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	// _ "github.com/go-sql-driver/mysql" ← こっちでも動く
)

type DB struct {
	Host string
	Username string
	Password string
	DBName string
	Connection *gorm.DB
}

func NewDB() *DB {
	c := NewConfig()
	return newDB(&DB{
		Host: c.DB.Production.Host,
		Username: c.DB.Production.Username,
		Password: c.DB.Production.Password,
		DBName: c.DB.Production.DBName,
	})
}

/**
 * func Open(dialect string, args ...interface{}) (db *DB, err error)
 * https://godoc.org/github.com/jinzhu/gorm#Open
 */
func newDB(d *DB) *DB {
	//
	// ex) MySQL
	// https://github.com/go-sql-driver/mysql#examples
	//
	// ex) MySQL Parameters
	// https://github.com/go-sql-driver/mysql#parameters
	db, err := gorm.Open("mysql", d.Username + ":" + d.Password + "@tcp(" + d.Host + ")/" + d.DBName + "?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err.Error())
	}
	d.Connection = db
	return d
}

// Begin begins a transaction
func (db *DB) Begin() *gorm.DB {
	return db.Connection.Begin()
}

func (db *DB) Connect() *gorm.DB {
	return db.Connection
}

main.go

package main


import (
	"fmt"
	"time"

	"psychedelicnekopunch/go-sample/infrastructure"
)


/*
CREATE TABLE `users` (
  `id` int(10) UNSIGNED NOT NULL,
  `name` varchar(100) NOT NULL,
  `age` int(10) UNSIGNED NOT NULL,
  `is_student` tinyint(1) UNSIGNED NOT NULL,
  `created_at` int(10) UNSIGNED NOT NULL
);
*/
type Users struct {
	ID int `json:"id"`
	Name string `json:"name"`
	Age int `json:"age"`
	IsStudent bool `json:"isStudent"`
	CreatedAt int64 `json:"createdAt"`
}


func main() {

	d := infrastructure.NewDB()
	db := d.Connect()

	/**
	 * 直接SQL文を書く
	 */
	// テーブル一覧を取得する
	// Exec() を使うとエラーになる
	// Raw() は Users などのモデルを必要としない書き方ができる
	rows, err := db.Raw("show tables").Rows()
	if err != nil {
		panic(err.Error())
	}
	defer rows.Close()
	for rows.Next() {
		var table string
		if err := rows.Scan(&table); err != nil {
			panic(err.Error())
		}
		// テーブルを空にする
		// 返り値を必要としない SQL 文は Exec() 使った方がわかりやすいかも
		db.Exec("TRUNCATE TABLE test." + table)
	}


	/**
	 * Create: 作成
	 */
	user := Users{
		Name: "test",
		Age:  16,
		IsStudent: true,
		CreatedAt: time.Now().Unix(),
	}
	// NewRecord() は AUTO INCREMENT に対応している?ので ID が自動的に付与される
	if !db.NewRecord(&user) {
		panic("could not create new record")
	}
	if err := db.Create(&user).Error; err != nil {
		panic(err.Error())
	}


	/**
	 * Query: 参照系その1
	 */
	foundUsers := []Users{}
	// SELECT * FROM users;
	db.Find(&foundUsers)
	if len(foundUsers) == 0 {
		fmt.Printf("not found users")
	}
	fmt.Println(foundUsers)


	/**
	 * Save: 更新
	 */
	foundUser := Users{}
	db.First(&foundUser, user.ID)
	// 取得できなかったら ID が初期値の 0 になっている。
	if foundUser.ID == 0 {
		panic("user not found")
	}
	// age と is_student を更新する
	foundUser.Age = 25
	foundUser.IsStudent = false
	if err := db.Save(&foundUser).Error; err != nil {
		panic(err.Error())
	}


	/**
	 * Query2: 参照系その2
	 */
	foundUsers2 := []Users{}
	// SELECT * FROM users WHERE age > 20 AND is_student = 0;
	db.Where("age > ? and is_student = ?", 20, false).Find(&foundUsers2)
	if len(foundUsers2) == 0 {
		fmt.Printf("not found users")
	}
	fmt.Println(foundUsers2)
}

確認

$ go run main.go

[{1 test 16 true 1555567890}]
[{1 test 25 false 1555567890}]

関連投稿