こんにちは。文系SEです。何もわからずに生きています。
先日フラグ管理をしようとした際に、非厳密な比較演算子を使って失敗してしまったので、そのことを書きます。
私のように何もわからずに生きているSEがいたら読んでください。
環境情報
Strutsがベースの現場独自のフレームワークで起こった出来事です。
DBはMySQLです。
やりたかったこと
DBにtinyint(1)で登録されているカラムの値が、1なら「表示」、0なら「非表示」という文字に変換して画面に出す!
言葉だとわかりにくいと思うので、下のイメージを見てくださいませ。
No. | 本文 | 表示 |
---|---|---|
1 | DBに登録されている値はtrue | true |
2 | DBに登録されている値はfalse | false |
こちらの表の中身は、それぞれDBからとってきた値です。
表示フラグにご注目です。
表示フラグの欄には、DBから取得してきたtrueかfalseの値が入るようにしてあります。
しかし、今回はtrueとfalseを、それぞれ表示と非表示という文字に置き換えたいので、下の表のようにしたいと思います。
No. | 本文 | 表示 |
---|---|---|
1 | DBに登録されている値はtrue | 表示 |
2 | DBに登録されている値はfalse | 非表示 |
ですので、今回やるべきは
DBの値がtrueの時は表示、そうでないときは非表示にする的な処理
の実装ですね
ちなみに私はtinyint(1)の意味を先日初めて知ったので、こちらに書いておきました。知らん方は読んでみてください。
↓公式のちゃんとしたやつ
これらの型は TINYINT(1) のシノニムです。 ゼロの値は false と見なされます。 ゼロ以外の値は true と見なされます。
↓私が書いた簡単なやつ
私が書いたほうは私が書いたのでゆるゆるです。
きちんと知りたい方ではなくて、とにかくふわっとしりたいよ〜な方向けです。
まずやったこと(ダメな例)
まだ赤ちゃんだった私は以下のようなコードを書きました。
<c:when test="${testData['displayFlag'] eq 1}"> 表示 </c:when>
<c:otherwise> 非表示 </c:otherwise>
testData['displayFlag']の値と1をくらべたのは、DBのカラムに1が入っていたからです。
結果(失敗)
画面を見に行くとエラーになっていて、何も表示されなくなりました…。
次にやったこと(ダメな例)
やっぱり赤ちゃんだった私は以下のようなコードを書きました。
<c:when test="${testData['displayFlag'] eq '1'}"> 表示 </c:when>
<c:otherwise> 非表示 </c:otherwise>
変えた部分
eq '1'
testData['displayFlag']の値と'1'をくらべたのは、なんとなくです。
頭がおかしくなって、文字列を数字と勘違いしたのかなあと思いとりあえず試したわけです。
それでは画面を見てみましょう。
今度はエラーになっていませんでした!!
でも…待って…
No. | 本文 | 表示 |
---|---|---|
1 | DBに登録されている値はtrue | 非表示 |
2 | DBに登録されている値はfalse | 表示 |
表示と非表示が逆になっとる…。
(ちがうちがう、そうじゃ、そうじゃない♪)
結果(失敗)
画面を確認しに行くと、画面に表示と出るはずのデータが非表示と出ていました。また、非表示と出るはずのデータが表示と出ていました。
つまり、君の名は状態だったわけです。
普通に失敗ですね。
最後にやったこと(よい例)
<c:when test="${testData['displayFlag'] eq true}"> 表示 </c:when>
<c:otherwise> 非表示 </c:otherwise>
変えた部分
eq true
右辺を文字列型から論理型に変えて、左辺と型を揃えました。
testData['displayFlag'] の正体をログで確認したら、DB上1と0に見えていた値がtrueとfalseで返ってきていたからです。
私はここではじめてtyniint(1)について理解したわけでございます。
もっと早く中身をログで確認しておくべきでしたね…。
では、画面を確認してみます。
No. | 本文 | 表示 |
---|---|---|
1 | DBに登録されている値はtrue | 表示 |
2 | DBに登録されている値はfalse | 非表示 |
これだーッ!これをやりたかったんだよ~。
結果(成功)
画面を確認してみると、trueのデータは「表示」、falseのデータは「非表示」となっていました。
成功です。
ヨシ!(現場猫)
原因
こうなってしまったのは、暗黙的な型変換が行われたせいです。
「eq」すなわち「==」は、非厳密な比較演算子ですから、型が一致しないものたちを比較することができます。
冒頭でtinyint(1)がboolean型になると書きました。
つまり、今回は論理型と文字列型を比較しており、例によって型変換が行われたはずです。
そして、この変換の結果が予期せぬ挙動を生んだと考えられます。
非厳密な比較演算子の暗黙的な型変換が都合よく行われるパターンもありますが、今回はうまくいかないパターンだったようです。
まとめ
あたりまえ体操ですが、非厳密な比較演算子を使う際は、両辺の型を揃えるのがよさそうです。
暗黙の型変換で予期せぬ結果が返ることを防ぐためです。
私は普段ぼんやり使っていたので、実害が出て初めて意識するようになりましたが…。
残る課題
- 良くない例の時に「表示」と「非表示」が入れ替わって表示された件
無効な比較が行われているのに「表示」が出てくるのはそれはそれでおかしいので、調査したいです。 - MySQL特有の挙動なのかの確認
PostgreSQLや他のクエリ言語では発生しないかも確認できるといいよってパイセンからアドバイスいただいたので、これも調査したいです!
初めてのQiita、皆さんみたいに上手には書けなかったけど、ここまで読んでもらえてうれしいです!
ありがとうございました。
さよならー