イノベーション エンジニアブログ


株式会社イノベーションのエンジニアたちの技術系ブログです。ITトレンド・List Finderの開発をベースに、業務外での技術研究などもブログとして発信していってます!


このエントリーをはてなブックマークに追加

GoのORマッパーを使ってみよう!

こんにちは。 エンジニアのNew塚本です。

10月から歯医者に通い始めて3ヶ月過ちましたが、未だ完治しておりません。
歯を削るキュイーンって音が大の苦手で、苦手というかムリなんです・・・
あの音、なんとかならないでしょうかねー

さて、今回は、初めてのGolangとORマッパーのgormを使ってみました。

目的

gormライブラリを使って簡単なDB操作を試してみます。

ライブラリの詳細は以下のサイトを参照ください。
http://jinzhu.me/gorm/

【DB接続】
import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres"
)

gormパッケージからインタフェースを通してpostgresパッケージは使用されます。Goでは、直接使用しないimport文があるとコンパイルエラーとなるため、ブランク修飾子を付けこれを回避します。 使われない変数についても同様にコンパイルエラーとなります。コンパイル言語なので厳格でいい感じです。

func test() {
	// DBコネクション設定
	db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
	if err != nil {
		panic(err)
	}
	// DB操作の実処理

	// コネクション解放
	defer db.Close()
}

DBコネクションの設定はこれだけ。

db.Close()にdeferキーワードを指定していますが、これは遅延実行を意味します。上記サンプルでは、DBコネクションに失敗した場合にpanic()が発生します。その後、必ず、db.Close()が実行されます。javaのfinally句みたいな感じですね。

ちょっとだけ内部処理(gorm.Open)を確認。"database/sql"のライブラリを使用してDBコネクションを生成、コネクションプールの管理もしているようです。オープンソースって楽しい!

【Create編】

テーブルとのマッピングはmodelパッケージに、Tb_invoiceという構造体を作りました。 構造体の名称、メンバ変数の先頭文字が大文字となっているのは、別パッケージから参照可能な変数とするためです。

package model
type Tb_invoice struct {
	Target_month int
	Client_id int
	Price int
}
func Create() {
	// DBコネクション設定
	db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
	if err != nil {
		panic(err)
	}

	// DB登録のための構造体
	invoice := model.Tb_invoice{}
	invoice.Target_month = 201711
	invoice.Client_id = 999999
	invoice.Price = 50000

	db.Create(&invoice)

    	// コネクション解放
	defer db.Close()
}

Create関数に登録する情報を設定した構造体のポインタを渡すだけです。

【Read編 Find】
func Find() {
	// DBコネクション設定
	db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
	if err != nil {
		panic(err)
	}

	invoice := []model.Tb_invoice{}

	db.Find(&invoice)

	// コネクション解放
	defer db.Close()
}

全件検索するFind関数です。返却値は複数件となるため、構造体配列へのポインタを渡します。

【Read編 Find+Where】
func FindWhere() {
	// DBコネクション設定
	db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
	if err != nil {
		panic(err)
	}
	invoice := []model.Tb_invoice{}
	conditions := model.Tb_invoice{}
	conditions.Target_month = "999999"

	db.Where(&conditions).Find(&invoice)

	// コネクション解放
	defer db.Close()
}

Find関数に条件を指定することができます。

【Update編】
func Update() {
	// DBコネクション設定
	db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
	if err != nil {
		panic(err)
	}

	invoice := model.Tb_invoice{}

	// 更新データ
	data := invoice
	data.Client_id = 222

	db.Model(&invoice).Where("target_month = ?", "999999").Update(&data)

	// コネクション解放
	defer db.Close()
}

Model関数を使います。
更新テーブルのインタフェースに対して、更新する値を指定したポインタをUpdate関数に設定するようです。
Where関数のインタフェースは、プリペアードステートメント方式でも可能でした。

【Join/Query発行】

テーブル結合

joinResult := []model.JoinResult{}

//select項目
col := "a.id, a.hoge, b.color"

//基礎テーブルを指定
db.Table("tableA a").Select(col).

//結合表を指定
Joins("inner join tableB b on (a.id = b.id)").
Where("a.delete_flag = ?", 0).
Order("a.id asc").
Scan(&joinResult)

SQL の直書き

multiResult := []client.MultiResult{}

// 発行するSQL sqlとmodelの変数はキャメルケース
sql := "実行するSQLを記載"
db.Raw(sql).
Scan(&multiResult)

テーブル結合や、クエリビルダを使用しないSQLもこんな書き方で実行できます。

【トランザクション管理】
// コネクション設定
db, err := gorm.Open("postgres", "user=hoge password=hoge dbname=testdb sslmode=disable")
if err != nil {
	panic(err)
}

// トランザクション開始
mTx := db.Begin()

invoice := model.Tb_invoice{}
invoice.Create_date = time.Now()

if err := mTx.Create(&invoice).Error; err != nil {
	fmt.Println("err : ", err)
	mTx.Rollback()
} else {
	mTx.Commit()
}
// トランザクション解放
defer mTx.Close()

トランザクションを使う場合も簡単に実装できました。

感想

Goのお作法に若干戸惑いましたが、全般的には直感的に書けるので描きやすい言語だと思います。そして、標準ライブラリ(database/sql)との違いですが、標準ライブラリは、Scan関数にマッピングする項目を指定しますが、gormではマッピングする構造体のポインタを渡すだけです。また、db.Debug()にすると、発行するSQLを出力してくれます。 簡単な検証でしたがこれは使えそうです。

おわり