golang

[Golang]postges接続とCRUD実行+SQL/XML実装

Golangで実装していて、postgres接続することになりました。

ORMapperはgorp、gorm、sqlxを試してみましたが、
どれもJavaのMyBatis的なSQL/XML機能は備えてなさそう。

仕方ないので一旦は一番汎用性が高そうなdatabase/sqlで作成することにしました。

それを拡張して期待の形にすることにします。

最初は簡単に単純な接続から行っていきます。

事前準備

postgreSqlにデータ投入

test_tbテーブルを使用します。

create database test_db;
\c test_db;
create table test_tb (id serial, value varchar(10));
insert into test_tb (value) values ('hoge'), ('fuga'), ('piyo');
select * from test_tb;

結果はこんな感じです。

SQL接続処理を書いてみる

できました。エラー処理はprintしてるだけなのでTODOにしてます。

main.go

package main

import (
	"database/sql"
	"fmt"

	_ "github.com/lib/pq"
)

var db *sql.DB

// test_tbのレコード
type TestTb struct {
	id    int
	value string
}

// エントリーポイント
func main() {
	tmp, err := sql.Open("postgres", "host=localhost port=5432 user=postgres password=postgres dbname=test_db sslmode=disable")
	db = tmp         // グローバルにそのまま代入できないのでtmpに一時保存
	defer db.Close() // 当メソッドの終わりに、dbをクローズする
	if err != nil {
		// TODO エラー処理
		fmt.Println(err)
	}
	execute()
}

// 処理実行
func execute() {
	insRet := regist("INSERT INTO test_tb(id, value) VALUES($1,$2) RETURNING id", 10, "poo")
	fmt.Println(insRet)

	updRet := regist("UPDATE test_tb SET value=$1 WHERE id=2 RETURNING id", "aaa")
	fmt.Println(updRet)

	delRet := regist("DELETE from test_tb WHERE id=$1 RETURNING id", 1)
	fmt.Println(delRet)

	rows := rows("SELECT * FROM test_tb")

	var records []TestTb
	for rows.Next() {
		var record TestTb
		rows.Scan(&record.id, &record.value)
		records = append(records, record)
	}
	fmt.Printf("%v", records)
}

// 更新系SQL用
func regist(query string, args ...interface{}) string {
	var id string
	err := db.QueryRow(query, args...).Scan(&id)
	if err != nil {
		// TODO エラー処理
		fmt.Println(id)
	}
	return id
}

// 参照系SQL用
func rows(query string, args ...interface{}) *sql.Rows {
	rows, err := db.Query(query, args...)
	if err != nil {
		// TODO エラー処理
		fmt.Println(err)
	}
	return rows
}

[実行結果]
10
2
1
[{3 piyo} {10 poo} {2 aaa}]

期待動作となりました。

やっぱ生のSQLは楽ですね。

ただ、これだとSQLが長いときに可読性が落ちるし、SQLの修正の時にコピペでできないなー。

ってことで冒頭にもある通りMyBatisのようにxmlを読み込んでsql実行できるようにしてみます。

疑似MyBatis(笑)の作成

SQL/XMLを作成する

main.goと同階層に配置します。

sql.xml

<querys>
  <query><id>insertTest</id><value>
INSERT 
INTO test_tb(id, value) 
VALUES ($1, $2) RETURNING id
  </value></query>

  <query><id>updateTest</id><value>
UPDATE test_tb 
SET
  value = $1 
WHERE
  id = 2 RETURNING id
  </value></query>
  
  <query><id>deleteTest</id><value>
DELETE 
from
  test_tb 
WHERE
  id = $1 RETURNING id
  </value></query>

  <query><id>selectTest</id><value>
SELECT
  * 
FROM
  test_tb
  </value></query>
  
</querys>

go処理を拡張する

↓修正箇所に★を付けてます。

main.go

package main

import (
	"database/sql"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"

	_ "github.com/lib/pq"
)

var db *sql.DB
var queryByte []byte

// test_tbのレコード
type TestTb struct {
	id    int
	value string
}

type Querys struct {
	Query []Query `xml:"query"`
}

type Query struct {
	Id    string `xml:"id"`
	Value string `xml:"value"`
}

// エントリーポイント
func main() {
	tmp, err := sql.Open("postgres", "host=localhost port=5432 user=postgres password=postgres dbname=test_db sslmode=disable")
	db = tmp         // グローバルにそのまま代入できないのでtmpに一時保存
	defer db.Close() // 当メソッドの終わりに、dbをクローズする
	if err != nil {
		// TODO エラー処理
		fmt.Println(err)
	}
	execute()
}

// 処理実行
func execute() {
	// ★引数をsqlId名に変更
	insRet := regist("insertTest", 11, "hyo")
	fmt.Println(insRet)

	updRet := regist("updateTest", "bbb")
	fmt.Println(updRet)

	delRet := regist("deleteTest", 3)
	fmt.Println(delRet)

	rows := rows("selectTest")

	var records []TestTb
	for rows.Next() {
		var record TestTb
		rows.Scan(&record.id, &record.value)
		records = append(records, record)
	}
	fmt.Printf("%v", records)
}

// 更新系SQL用
func regist(sqlId string, args ...interface{}) string {
	var id string
	// ★xml読み込みメソッド呼び出し
	err := db.QueryRow(readQuery(sqlId), args...).Scan(&id)
	if err != nil {
		// TODO エラー処理
		fmt.Println(id)
	}
	return id
}

// 参照系SQL用
func rows(sqlId string, args ...interface{}) *sql.Rows {
	// ★xml読み込みメソッド呼び出し
	rows, err := db.Query(readQuery(sqlId), args...)
	if err != nil {
		// TODO エラー処理
		fmt.Println(err)
	}
	return rows
}

// ★xml読み込みメソッド
// SQL取得
func readQuery(sqlId string) string {

	// xmlファイル読み込み
	if queryByte == nil {
		file, err := os.Open("sql.xml")
		if err != nil {
			// TODO エラー処理
			fmt.Println(err)
		}
		defer file.Close()

		data, err := ioutil.ReadAll(file)
		if err != nil {
			// TODO エラー処理
			fmt.Println(err)
		}
		queryByte = data
	}
	v := Querys{Query: nil}
	err := xml.Unmarshal(queryByte, &v)
	if err != nil {
		// TODO エラー処理
		fmt.Println(err)
	}

	for _, query := range v.Query {
		if query.Id == sqlId {
			return query.Value
		}
	}
	// TODO エラー処理
	return ""
}

これで期待した形になりました。

xmlをA5Mk2とかでフォーマットしたものをそのままコピペできるので、非常に使いやすいです。

最後に

GolangのORMapperをラッピングするともっとうまいことができそうですね。

時間ができたら書いてみようかな。

ご意見、ご感想等ございましたら、↓よりコメントお願いします。励みになります。

以上です。今後ともよろしくお願いします。

-golang

Copyright© 婿入りエンジニア、ブログ書く , 2019 All Rights Reserved.