Open4

GoのORM sqllaの使い方

macopymacopy

所属している会社内ではいくつかのプロジェクトで使われている自作のORM sqllaというものがあるが、これについて日本語でまとめた記事が最近ではないので、雑にまとめてみる。

ライブラリ自体はこちら
https://github.com/mackee/go-sqlla

macopymacopy

sqllaの特徴

  • MySQLおよびsqlite向けの一部のSQLを扱うことが出来る
  • structでテーブルスキーマを定義して、それをベースにクエリビルダーのコードを生成する
  • クエリビルダーで生成したSQLの結果をテーブルスキーマstructに対してマッピングが行える
  • DB接続の管理は行わない
macopymacopy

sqllaでのスキーマ定義とコード生成

まず、以下のようなテーブルがあるとする。

ちなみにここからはMySQL前提で説明する。

CREATE TABLE `task` (
      `id` BIGINT unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
      `name` VARCHAR(191) NOT NULL,
      `description` TEXT NOT NULL,
      `status` INTEGER unsigned NOT NULL DEFAULT 1,
      `created_at` DATETIME NOT NULL,
      `updated_at` DATETIME NOT NULL
);

このスキーマをsqlla向けに書いたGoのstructにすると以下のようになる。

type Task struct {
	ID          uint64    `db:"id,primarykey"`
	Name        string    `db:"name"`
	Description string    `db:"description"`
	Status      uint32    `db:"status"`
	CreatedAt   time.Time `db:"created_at"`
	UpdatedAt   time.Time `db:"updated_at"`
}

このstructを定義したpackageで以下のgo generateの定義を書く

//go:generate sqlla

go generateを行う前に、以下のコマンドでsqllaコマンドを使えるようにしておく

$ go install github.com/mackee/go-sqlla/v2/cmd/sqlla@v2.10.0

そして、go generateを行う

$ go generate

するとtaskテーブルを扱うためのクエリビルダーとマッピングメソッドが収まるコードが生成される。この場合はtask.gen.goが生成されている。

macopymacopy

生成されたコードを用いたSELECT

生成されたコードと同じパッケージで使う前提でSELECTを行うコードを書く。ここではmain packageに生成した前提で書いている。

package main

import (
	"context"
	"database/sql"
	"fmt"

	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := sql.Open("mysql", "user:password@/dbname")
	if err != nil {
		panic(err)
	}

	ctx := context.Background()
	task, err := NewTaskSQL().Select().ID(1).SingleContext(ctx, db)
	fmt.Printf("%#v\n", task)
}

完全な動作するコードを提示したが、ポイントとしては、

  • database/sqlをそのまま用いるときと同じように*sql.DBを作る
  • NewTaskSQL().Select()でTaskテーブルに対するSELECT文を生成するクエリビルダーを作成
  • クエリビルダーのID(1)メソッドがWHERE id = 1に相当する
  • SingleContextでクエリを実行し、返ってきた結果を前述したTask structへのマッピングを行って返す

となっている

ID以外にもstructに定義したfieldで絞り込みを行うメソッドが生えている

	task, err := NewTaskSQL().Select().
		Name("foobar").
		Description("Lorem Ipsum").
		Status(1).
		CreatedAt(time.Now()).
		UpdatedAt(time.Now().Add(12*time.Hour)).
		SingleContext(ctx, db)