SQLBoilerでInnerJoinやLeftJoinをしたいとき、qmパッケージのInnerJoinとLeftJoinなどを使いがちです。
ただここで問題があって、
このqmパッケージの関数を使ってJoinしようとすると以下のように書けます。
models.Users(
qm.InnerJoin("posts ON posts.user_id = users.id"),
).All(ctx, db)
つまり、生のSQL文字列を書く必要があるのです。
何が問題なのかというと、
可読性が低い
テーブル名やカラムを直書きしている時点でSQLBoilerを活用しきれていない
タイポの温床となる
DRYじゃない
同じような結合条件があると、このような文字列を大量に書く必要がある
正直今上げた問題点はかなりコードを書く上では辛いです。
ではどうするか?
関数化しておこう!
よく使うJoinのパターンを関数化(ヘルパー化)しておきましょう。
毎回 fmt.Sprintf や生の文字列を書くのではなく、構造化された引数を受け取る関数を用意することで、コードの見通しが良くなります。
例えば、以下のようなInnerJoin関数を作っておけば、
func InnerJoin(joinTable, baseTable, joinTableColumn, baseTableColumn string) qm.QueryMod {
return qm.InnerJoin(fmt.Sprintf("%s ON %s.%s = %s.%s",
joinTable, // joinTable: 結合するテーブル名
joinTable,
joinTableColumn, // joinTableColumn:結合するテーブルのカラム
baseTable, // baseTable: 結合元のテーブル名
baseTableColumn, // baseTableColumn: 結合元のテーブルのカラム
))
}
以下のように使うことができます。
以下はTagsテーブルに対して、ContentsTagsテーブルをInnerJoinしたい時の例です。
models, err := models.Tags(
InnerJoin(
models.TableNames.ContentsTags,
models.TableNames.Tags,
models.ContentsTagColumns.TagID,
models.TagColumns.ID,
),
このように書くことで次のようなメリットが得られます。
- 安全性の向上
- SQLBoilerが生成する
models.TableNamesやmodels.TagColumnsといった定数と組み合わせることで、文字列のタイポを撲滅できます
- SQLBoilerが生成する
- 変更に強い
- 万が一カラム名が変更になっても、定数を使っていればコンパイルエラーで検知できる可能性が高まります
- 可読性
- 「どのテーブル」「どのカラム」で結合しているかが引数として明確になります
LeftJoinやRightJoinも同様に関数化しておけばかなり便利です
まとめ
SQLBoilerのqmパッケージでJoinを行う際、生のSQL文字列を直書きするとタイポや変更漏れのリスクが生じます。
この課題を解決するために、SQLBoilerが生成するテーブル名やカラム名の定数を引数に取れるヘルパー関数を自作すること型安全性や可読性の高いコードを実現できます。