1. Qiita
  2. 投稿
  3. Go

reflectフリーなクエリビルダーsqllaにORM-likeなメソッドを追加した

  • 1
    いいね
  • 0
    コメント

sqllaとは

https://github.com/mackee/go-sqlla

  • Go向けのクエリビルダー
  • 事前にDBのスキーマをstruct tagを用いて定義してクエリビルダーのコードを自動生成
$ go get github.com/mackee/go-sqlla/cmd/sqlla

//go:generate sqlla

//+table: user
type User struct {
    ID      uint64 `db:"user"`
    Name    string `db:"name"`
    Age     uint32 `db:"age"`
    IsAlive bool   `db:"is_alive"`
}
$ go generate

コード生成のメリット その1

  • 型解決がコンパイル時にできる
    • スキーマ定義とは違う型の値を入れようとするとコンパイルエラー
    • reflectを使わなくても良いのでreflectを使うクエリビルダー(github.com/Masterminds/squirrel など)に比べて高速

コード生成のメリット その2

  • 補完が効く
    • 当然型も表示される https://gyazo.com/a6e77e30a8ca5d774bb8969c5c06f861

ここまでは今まで出来ていたこと


どのように使っていたかというと

query, args, err := NewUserSQL().Select().ID(uint64(1)).ToSql()
if err != nil {
    return fmt.Errorf("unexpected user query build error: %s", err)
}
row := db.QueryRow(query, args...)
err = row.Scan(&u.ID, &u.Name, &u.Age)
if err != nil {
    return fmt.Errorf("user query execute error: %s", err)
}

:innocent: だるい :innocent:


ORM-like methods


一般的なORMの機能

  • オブジェクト/構造体への結果のマッピング
  • オブジェクト/構造体に対応するDB内のデータへの操作メソッドの付加
  • 言語の構文を利用したSQLの組み立て
  • 接続/トランザクション管理(コネクションプール / nested transactionなど)
  • リレーション
  • schemaのmigration

今までのsqllaの機能

  • オブジェクト/構造体への結果のマッピング
  • オブジェクト/構造体に対応するDB内のデータへの操作メソッドの付加
  • 言語の構文を利用したSQLの組み立て
  • 接続/トランザクション管理(コネクションプール / nested transactionなど)
  • リレーション
  • schemaのmigration

sqllaのORM-like method

  • オブジェクト/構造体への結果のマッピング <- New!
  • オブジェクト/構造体に対応するDB内のデータへの操作メソッドの付加 <- New!
  • 言語の構文を利用したSQLの組み立て
  • 接続/トランザクション管理(コネクションプール / nested transactionなど)
  • リレーション
  • schemaのmigration

クエリを組み立てて直接structを取得

u, err := NewUserSQL().Select().ID(uint64(1)).Single(db)

複数行を返すクエリ

// SELECT * FROM user WHERE is_alive = 1;
users, _ := NewUserSQL().Select().IsAlive(true).All(db)
for _, u := range users {
    fmt.Printf("id: %d, name: %s\n", u.ID, u.Name)
}

構造体からUPDATE文の発行

u, _ := NewUserSQL().Select().ID(uint64(1)).Single(db)

// UPDATE user SET is_alive = 0 WHERE id = 1;
// SELECT * FROM user WHERE id = 1;
uu, _ := u.Update().SetIsAlive(false).Exec(db)

DELETE文

u, _ := NewUserSQL().Select().ID(uint64(1)).Single(db)

// DELETE FROM user WHERE id = 1;
err := u.Delete(db)

:innocent: これで十分やろ :innocent:


まだ不十分かつ入れたいと思っているもの

  • リレーション
    • 単純なアソシエーションメソッドではなくJOINで一気にとってくるやつ
  • ORとCASEとHAVING
  • Prepare
  • 英語のドキュメント
  • メモリアロケーション削減

そんなわけで頼む使って見てくれ頼む:pray: