環境
- 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}]