DBFlute-1.1になったらどうなるシリーズ、
前回のに引き続きです。
DBFluteのJava8版というか1.1ではこうなる
今回は、地味ながらインパクト大きい話をします。
早速ズバリ...
CBでnullや空文字入れたら例外に!
します。(デフォルトで)
決断いたしました。
前回のブログで「まだ悩んでるのも」として、
取り上げていました。
MemberCB cb = new MemerCB();
cb.query().setFoo_Equal(null);
cb.query().setFoo_Equal("");
cb.query().setFoo_InScope(空リスト);
cb.checkInvalidQuery();
cb.query().setFoo_Equal(null);
MemberCB cb = new MemerCB();
cb.query().setFoo_Equal(null);
cb.query().setFoo_Equal("");
cb.query().setFoo_InScope(空リスト);
cb.ignoreNullOrEmptyQuery();
cb.query().setFoo_Equal(null);
1.0.x系では、デフォルトが素通り (条件無視) でした。
何も設定しなかったのと全く同じです。
これは、検索画面での条件設定にフィットします。
テキストに何も入力されなければ条件にしない、
入力されたら有効な条件にする、という挙動がぴったり。
ですが、ロジック内での検索ではあまり意味がなく、
それどころか「全件検索事件」を招いてしまう可能性があると。
cb.query().setMemberId_InScope(list);
List<Member> memberList = memberBhv.selectList(cb);
memberBhv.batchDelete(memberList);
queryDelete()であれば全件削除は許されていません。
そもそもパフォーマンス的にも実装的にも
queryDelete() の方がgoodですが、
固定条件が一つでもあったらやっぱり削除できてしまいます。
cb.query().setMemberId_InScope(list);
cb.query().setBirthdate_IsNull();
memberBhv.queryDelete(cb);
引数チェックのジレンマ
プログラミングを長くやってきた一個人としては、
それなりにでも引数チェックをしてほしい、
という気持ちがあります。
jfluteが若い頃、師匠から、
「外から来たデータは絶対に信用するな」
と教えられました。
常に引数の入出力では「あるのかないのか」を意識して、
「必要であれば引数チェックを、念のためのチェックを」
という感覚で、
「なのでそういうもんだから今の仕様で問題ないだろう、
というか、とにかく検索画面で便利なんだから」
と正直、安易に考えてしまっていました。
いまでも、引数チェックに対する価値は同じく持っています。
jfluteが研修・フォローイングしている現場では、
耳にタコが引っ付くほど聞かされている話かと思います。
ですが、数年に一度ほど全件検索事件が耳に入ると、
「ええ...なんでチェックしないのぉー(><」
という落胆の思いと、
「DBFluteでなぜ防いてあげられないか...」
というjflute自身に対する怒りの思いの板挟み。
引数チェックは必要、でもそれは理想、
こういうことは他にもたくさんあるかと思います。
そこに全て寄りかかってはいけない。
最後の砦として、
ConditionBean が安心できる
インターフェースになるべきだろうと。
DBFlute、というかConditionBeanの特徴、
それはやはり「安心・安全・スピード」です。
DBFluteはやっぱりここがいい、的な話をしてくれたとき、
多くのDBFluteユーザーからもらっている言葉です。
安全だから安心で、スピードが出せる
DBFluteは、とても弱いフレームワークです。
これからも現場で活躍していくためには、
しっかりと「DBFluteとは?」をjflute自身も見極めて、
将来のためにその特徴を最大限活かす仕様にしようと決めました。
たくさん、たくさん、考えました。
デフォルトって重要
cb.ignoreNullOrEmptyQuery()を用意しても、
その存在に気付かない人というのは必ずいらっしゃいます。
「if文書かないとエラーになるってうざいなぁ」
(いやそれは ignoreNullってのがあってですねー><)
「えっ、わざわざ ignoreNull とかやんなきゃいけないの?」
って言われるのは目に見えてますが、
if文を書き過ぎても大きなトラブルにはつながりにくいですが、
ノーチェック検索は大きなトラブルにつながりやすい。
なので、覚悟します。
ダサいインターフェースと言ってくださいm(_ _)m。
昔、Railsの作者の方が雑誌のインタビューで言っていた言葉を
何度も思い出します。
「デフォルトっていうものがどれほど重要か、みなわかってない」
と、ちょっと昔のこと過ぎてニュアンスが正確じゃないですが、
DBFluteを運用してきてずっとその言葉が身に沁みるのです。
もとに戻せます
もちろん、1.0.x系からの移行のためにも、
dfpropでデフォルト挙動をを簡単に戻せます。
littleAdjustmentMap.dfprop にて、
isNullOrEmptyQueryAllowed で変更できます。
; isNullOrEmptyQueryAllowed = true
で、デフォルトで Allowed にした上で、
ここの ConditionBean だけはチェックしたい、
というときは、
MemberCB cb = new MemberCB();
cb.checkNullOrEmptyQuery();
とすれば、チェックされるようになります。
既にある checkInvalidQuery() と同じです。
もし、こちらのメソッドをたくさん使っていて、
移行が大変であれば、同じくdfpropにて、
isCompatibleConditionBeanOldNamingCheckInvalid
で、古い名前のメソッドを復活させることができます。
画面の検索条件のときは ignoreNull
まあ、とにかく今後は、
(dfpropで特に何も設定していなければ)
画面入力の検索条件のときはこれを呼ぶ!
cb.ignoreNullOrEmptyQuery()
と思って頂ければと。
ignoreで始まるメソッドは、これだけです。
誤解の無いように「無視する」という表現から
そのまま取りました。
また、この null や空文字の無効なQueryに対して、
もともと InvalidQuery という概念名を付けていましたが、
NullOrEmptyというベタベタな名前をあえて付けました。
これを知らないプログラマーがソースを見たとき、
想像が付きやすいように、目に付きやすいように。
これを知らないプログラマーがメソッド補完したとき、
「欲しいの、これじゃないかな?」
って言う風に、気が付きやすいように。
単純で覚えやすいように
-> nullと空文字のquery()を無視
誤解がないように
-> あくまでquery()のお話、空文字も含む
言葉で発しやすいように(!?)
-> いぐのあーぬる (おあえんぷてぃー)
長過ぎず短すぎずでこの名前になりました。
細かいけどFromToのお話
関連して、
FromToの「片側だけ設定」の扱いがちょっと変わります。
MemberCB cb = new MemerCB();
FromToOption option = new FromToOption().compareAsDate();
cb.query().setXxx_FromTo(null, 2014-09-26, option);
cb.query().setXxx_FromTo(2014-09-26, null, option);
cb.query().setXxx_FromTo(null, null, option);
option.allowOneSide();
cb.query().setXxx_FromTo(null, 2014-09-26, option);
cb.query().setXxx_FromTo(2014-09-26, null, option);
cb.query().setXxx_FromTo(null, null, option);
cb.ignoreNullOrEmptyQuery();
cb.query().setXxx_FromTo(null, null, option);
Queryのチェックがあろうがなかろうが、
今まではFromToでの片側だけの設定が許されていましたが、
デフォルトでは、許されなくなります。
これも Strict にするポリシーの一環です。
明示的に allowOneSide() とやった場合のみ片側設定できます。
こちらも、もちろん dfprop で元に戻せます。
littleAdjustmentMap.dfpropにて、
isCompatibleConditionBeanFromToOneSideAllowed
で、チェック状態でも片側に null を入れることができます。
; isCompatibleConditionBeanFromToOneSideAllowed = true
上書き設定も例外に
もう一つ、Strictにした仕様があります。
上書き禁止です。
#
# 1.1
#
MemberCB cb = new MemerCB();
cb.query().setXxx_Equal(3);
cb.query().setXxx_Equal(4);
cb.enableOverridingQuery();
cb.query().setXxx_Equal(4);
cb.query().setXxx_Equal(5);
こちらはあんまりインパクトは少ないと思われますが、
時々この挙動に乗っかってる実装を見かけます。
意図しているならいいのですが、
予期せぬ上書きによるバグも時々見かけるので、
安全よりの方に倒させて頂きます。
「あれっ、したら ConditionBean の再利用は?」
上書きを使って ConditionBean を再利用するケースが
ありますが、どうしてもというなら enable メソッドで。
ですが、そもそもConditionBeanの再利用は、
ArrangeQuery がオススメです。
=> where句の再利用 | DBFlute
なので、デフォルトでは例外とさせて頂きます。
Strict but 戻せる
Strictなインターフェースを追求しながら、
でも、結局 dfprop で戻せるので互換は保てる、
(既存プロジェクトに適用できる)
というような感じです。
1.1の前に1.0.x
1.1の前に、これらのオプションを入れておいて、
まだ OFF の状態でリリースする 1.0.x を準備します。
1.0.x系でも新規の開発で1.1を見越しての利用であれば、
dfpropでこれらのプロパティに逆に false を設定して、
1.1と挙動に同じにしてもいいかもしれません。