つかびーの技術日記

情報系修士卒のWeb系技術日記です。現在のフォーカス分野はアドテクです。

MyBatisでDB上のいわゆる業務コードを、Java上のenumに結び付ける

   

enumは非常に便利です。CやJavaやC#のプログラマであれば、使わない手はありません。

ここではJavaのenumが対象ですが、なぜenumが良いのかは簡単に言うと型安全だからです。詳しくは7ステップで理解するJavaでの列挙型/enum使用法 (1/3) – ITあたりを参照してください。

JavaのenumとDBの問題

この便利なenumですが、残念ながらDBに出し入れする時には一苦労します。DBにはenumを直接格納・取り出しできないため、何らかの変換処理が必要だからです。

自分の経験した残念なプロジェクトではenumを利用していませんでした。ではどうしたかというと、Stringで代用していました。DB上に部門コードのようなものがあって”00″, “01”, …, “40”というような感じに格納していました。このString(VARCHAR2)をDB上の結合などにも使うし、コード上で使いたいときはSQLで取ってきて”01″.equals(bumonCode)などとやっていました。

残念ながら上記のような仕組みのせいで問題だらけでした。部門コードが2桁で”01″とかなってるけど、比較する部分では”1″と比較していてバグが発生したり。メソッドの始めにString CONST_1000 = “00”; (他多数、CONST_1100くらいまで。。。)とかするもんだから、後のコードでCONST_1000.equals(bumonCode)などとなっていて、さっぱり意味が分からなかったり。部門コードではないCONST_1050と比較するCONST_1050.equals(bumonCode)なんてバグもありました。色々と最悪でした。ちなみに自分はプロジェクトのテストフェーズから参画しましたが、デスマってました。

DB上のコードとJavaのenumを結びつける方法

そんな訳で、できればenumを利用したい所です。(上記の問題の例はenum以前の問題という気もしますが)

ではどうすれば良いか、というとEnum と データベースの「コード値」の相互変換 – penultimate diaryなんかは、良い例を挙げています。こういう仕組みを利用することが大切だと思います。

ここでは上記とほぼ同じ仕組み、つまりenumとDBのコード変換の仕組みを用意し、ORマッパーに自動変換させる手法を使います。上記のURLはHibernateですが、こちらではMyBatis用のコードです。

MyBatisなら変換の仕組みはデフォルト装備

実はMyBatisには上記のURLのような仕組みがデフォルトで備わっています。これを活用しない手はありません。

MyBatis – MyBatis 3 | 設定を見ると分かりますが、EnumTypeHandlerとEnumOrdinalTypeHandlerというものがあります。EnumTypeHandlerはDB上の値が先ほどの部門コードではなく部門名であるような場合に利用します。EnumOrdinalTypeHandlerはDB上の値が先ほどの部門コードのような場合に利用します。

それではこれを使ったサンプルを作成します。

まずテーブルを作成します。GENDER列とDEPARTMENT_ID列が今回のenumの対象です。

次にJavaのコードを作成します。

Gender.java

Department.java

Employee.java

EmployeeMapper.java

mybatis-config.xmlのconfigurationタグ内に以下を追加します。

最後にMain.javaです。

適当なレコードを挿入し、その後enumによって検索しています。enumを検索条件に指定できる点がポイントです。これによって型安全が実現できています。SELECTによって取得する時も同様で、enumで取れるため、Java側で安全に利用することができます。

これを実行すると、DB上には以下のような形式でデータが格納されます。

EMPLOYEE_ID NAME GENDER DEPARTMENT_ID
100 Tarou Male 0
101 Hanako Female 2

GENDERもDEPARTMENT_IDも同じenumですが、格納されている値の形式が異なります。これはGENDERにはEnumTypeHandlerを、DEMAPARTMENT_IDにはEnumOrdinalTypeHandlerを利用したためです。数値を利用することが多いと思うので、EnumOrdinalTypeHandlerを利用しましょう。DEPARTMENT_IDには0と2が入っていますが、これはenumであるDepartmentのordinalに依存しています。Tarouは総務部で総務部はDepartmentの最初の要素ですから0になります。Hanakoは営業部で営業部はDepartmentの3番目の要素ですから2になります。ordinal=indexと思っていいと思います。

コード体系変更の危険性とordinalの注意

実際のプロジェクトでこの仕組みは上手く動作すると思いますが、注意しなければならないシーンはやはりあります。

コードの体系を変えるというシーンはよくあるかと思います。例えば、上記の部門enumの要素は総務部と開発部と営業部でした。ここで顧客から「組織体系を改革します。部署も一新するため、コードを変更します。総務部=11, コンサルティング部=12, 営業部=13としてください。DB上のコードマスタとプログラムを改修してください。」というような要求があったとします。

残念ながら上記の仕組みはenumのordinalに依存していますから、enumに3つの部門要素を定義してそれぞれ11, 12, 13というようにはできません。やるとしたら、以下のような非常に見苦しいコードになることでしょう。

(コード体系変更された)Department.java

見苦しいですが、それでもintやStringよりかはenumを利用した方が良い気はしています。実際上記の要望を叶えるとDBの値変更、上記のenum変更、廃止された開発部(Development)固有のコードへの対応、新設されたコンサルティング部(Consulting)固有のコードへの対応、という感じになるでしょう。Padding0-10が嫌なだけで、それほど問題ではないかな・・・?

以上で終わりです。

enumが利用できるとプログラムの安全性が大きく高まります。MyBatisを利用する時は、ぜひ上記のようなenum変換の仕組みを利用しましょう。

 - Java