Doma2 の1件検索でスカラ値を取る場合に、ちょっと悩んだこと
Doma2 の1件検索はこちらを
検索 — Doma 2.0 ドキュメント 1件検索
悩んだのはこんなケース。
DB から 1行1項目(スカラ値)を取りたいけど、その項目は null の場合もあるし、そもそも条件にマッチする行が無い場合もある。
行が無い場合と、行はあるけど null の場合で、後続の処理が変わるというケース。
@Select Optional<String> getHoge(int id);
これだと 行がありデータも null でない時は良いけど、行が無い or データが null の時は、Optional#empty になってしまい判断出来ない。
これを回避するには幾つか方法があって、
- 戻り値の String を Wrap した DTO を作ってそれで受ける
- SelectOptions 使って件数取る
- ensureResult=true でデータ無い時は例外を投げる
1個目はこんなの。
@Data @Entity class WrapDto { @Column(name="HOGE") private final String value; public WrapDto(String value) { this.value = value; } } // Dao はこんな感じ @Select Optional<WrapDto> getHoge(int id);
WrapDto の field は Optional
データがあるけど、null の場合は、WrapDto の field が null*1で、判断出来る。
追記:
DTO を Domain クラスにしたら?という案が出てきましたが、Domain クラスの value はデフォルトでは not null のはずなので、今回の用途にはあわないかなと思います。
ドメインクラス — Doma 2.0 ドキュメント
ドキュメントにはそれらしい事載ってませんがソースで確認出来ます。
doma/Domain.java at master · domaframework/doma · GitHub
@Domain の acceptNull を true にすると null OK になるはず。*2
2個目はこんなの。
@Select Optional<String> getHoge(int id, SelectOptions options); // 使う方はこんな感じになる。 SelectOptions options = SelectOptions.get().count(); Optional<String> op = dao.getHoge(1, options); if (options.getCount() == 0) { // データが無いとき } else if (op.isPresent()) { // データがあって null で無い時 } else { // データがあるけど null の時 }
検索 — Doma 2.0 ドキュメント 集計
SelectOptions#count は 今のところクエリを2回投げる形になってるのでちょっと使いづらい感じ。
Doma が内部でこんな感じにクエリ書き換えてくれたら1回で済みそうだけど DB やバージョンの差異とか考えると面倒そう。
select ... , count(*) over() as __domaselectoptionscount from ... where ... order by ...
3個目はこんなの
@Select(ensureResult=true) Optional<String> getHoge(int id); // 使う方はこんな感じになる。 try { Optional<String> op = dao.getHoge(1); if (op.isPresent()) { // データがあって null で無い時 } else { // データがあるけど null の時 } } catch (NoResultException _) { // データが無かった時 }
検索 — Doma 2.0 ドキュメント 検索結果の保証
catch するのが嫌なのと処理がちょっと分かれるのもあれげな感。
処理を続けて書くならこんなの?
Optoinal<String>> result; try { result = Pair.of(true, dao.getHoge(1)); } catch (NoResultException _) { result = null; } if (result != null)) { if (result.getRight().isPresent()) { // データがあって null で無い時 } else { // データがあるけど null の時 } } else { // データが無い時 }
Optional に null 入れるのは何かやだなー。
久々の xtend ならもうちょいすっきり
val result = try { dao.getHoge(1) } catch (NoResultException _) { null } switch result { case null: // データが無いとき case Optional.empty: // データがあるけど null のとき default: // データがあるとき }
何個か書いたけど 1番目の DTO 作るのが良い気がしてます。