前置き
小ネタになります
最近Go言語でAPIを作っていてORMにSQLBoilerを採用しました。
少し複雑なWhere句をタイプセーフで書く方法の情報が意外に少ないのではないかと思い執筆しました。
テーブル構成
このようなテーブルがあったとします。
CREATE TABLE `foods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8mb4_bin NOT NULL,
`price` int(10) unsigned NOT NULL,
`note` varchar(45) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
生クエリ
少しも複雑なWhere句ではないと思いますが、このくらいの条件でもタイプセーフで書いていない記事が多いので執筆してます。
複雑なサブクエリなどを期待されていた方には申し訳ありません
SELECT
*
FROM
foods
WHERE
price >= 100 AND price < 500
AND (note = '特売' OR note IS NULL)
SQLBoiler
比較的よく紹介されている記述
このように紹介されていることが多い印象です。「簡易的にビルダーでクエリを組み立てる」とでも申しましょうか。
モデルにマッピングしてくれる利点はありますが、あまり生クエリと変わりなく筆者としてはもっとORMの恩恵を受けたいと思います。恩恵を受けたくないのならORMを採用しない選択肢もあると考えます。
res, err := models.Foods(
qm.Where("price >= ? AND price < ? AND (note = ? OR note IS NULL)", 100, 500, "特売")).
All(context.Background(), db.DB)
SELECT * FROM `foods` WHERE (price >= ? AND price < ? AND (note = ? OR note IS NULL));
[100 500 特売]
WHERE/AND/OR/()毎に組立てる記述
上記の表現、公式ドキュメントから名前を発見できず、読解力や語彙力がなく申し訳ありません
このように小分けに書くこともできます。比較的よく紹介されている記述
と比較したらORMの活用率は上がりましたね。AND
とかOR
、()
のtypoは減るかもしれません。筆者としてはもっとORMの恩恵を受けたいと思います。
res, err := models.Foods(
qm.Where("price >= ?", 100), qm.And("price < ?", 500),
qm.Expr(qm.And("note = ?", "特売"), qm.Or("note IS NULL"))).
All(context.Background(), db.DB)
SELECT * FROM `foods` WHERE price >= ? AND price < ? AND (note = ? OR note IS NULL);
[100 500 特売]
タイプセーフに組立てる記述
ORMの恩恵を多く受けたければ以下のように書けます。SQLBoilerの公式で強く推奨してる方法です。
「冗長的な記述だけど、タイプセーフでコンパイルエラーになるからいいよね」ということです。SQLBoilerに関わらず、他ORMでもこのような思想はありますね。
インテリセンス(コード補完)が活用できるのも魅力の1つだと感じます。
res, err := models.Foods(
models.FoodWhere.Price.GTE(100), models.FoodWhere.Price.LT(500),
qm.Expr(models.FoodWhere.Note.EQ(null.StringFrom("特売")), qm.Or2(models.FoodWhere.Note.IsNull()))).
All(context.Background(), db.DB)
SELECT * FROM `foods` WHERE `foods`.`price` >= ? AND `foods`.`price` < ? AND (`foods`.`note` = ? OR `foods`.`note` is null);
[100 500 {特売 true}]
終わりに
ORM自体の是非というのは揉め易いネタだと思いますので割愛します。そもそもORMを採用するにしてもフルスタックかマイクロの議論もあります。全てをORMに頼るのは難しく生クエリで書いたほうが良い場面もありますので「ORMで消耗する」のではなくバランス良く使って生産性や保守性、堅牢性を上げられれば良いかなと思っています。また、公式ドキュメントにタイプセーフな記述方法も書いてあるのですが調査中はわかりませんでした。コードを書き終えてから結果論として「あぁ、このことか」と理解しました。筆者の読解力の問題を棚に上げつつ、ドキュメントをもう少し親切にしてほしいと思っております。