2010年08月29日

JDBCドライバの差異 queryForMap()の返すMapのキー

またしても驚くべき事実に遭遇しました。
どうも PostgreSQL を普段から使用している方には当たり前のことのようですが、10年以上 Oracle 一辺倒で、最近 H2 で頑張っている私としては、衝撃の事実でした。

では何が衝撃の事実だったのか?
Spring freamework を利用して JDBC 経由でDBにアクセスしているのですが、queryForMap() で返されるMapのキーの文字列が、PostgreSQL を利用すると小文字で格納されます。
それまで H2 で動作していたものを PostgreSQL で動作させるため、SQL文の差異を修正して実行したところ、値が取れなかったため発覚しました。
今となっては結論がわかっているので当たり前ですが、当初はSQL文を疑ったり、そこへ至る分岐を疑ったりと、少し手間取ってしまいました。
Springを使用して実行したコードは下記のようになります。
JdbcTemplate db = getSimpleJdbcTemplate();
String sql 
    = "SELECT TO_CHAR(CURRENT_TIMESTAMP, 'yyyy/mm/dd') AS DSTMP";
Map value = db.queryForMap(sql);
String today = value.get("DSTMP");
実際に Map に詰められている内容は、「dstmp=2010/08/29」なわけですから、"DSTMP"でget()しても一致するキーがないので today には null が詰められるわけです。
もう少し丁寧に説明すると、各JDBCドライバが返すResultSetMetaDataに詰められているカラムラベルを用いてSpringがMapを作成します。
そのため、PostgreSQLのJDBCドライバが小文字で返すことにより、結果的に上記のような動作になった次第です。
SQL文に大文字でカラムのエイリアスまで切っているのに、わざわざ小文字に変換して返すとは想像だにできませんでした。

ちょこっと調査してみたところ、PostgreSQL は小文字のキーにして返します。
DB2は大文字・小文字を区別しているようなので、Mapに返すキーも大文字・小文字交じりになると予想します。(すみません確認できていません)

RDBMSに依存しない方法はないのでしょうか?
実は複数DBに対応したアプリを作成していて、それぞれのRDBMSの差異を吸収するようにしたいので、対策を考えてみます。
いくつか解決方法を挙げてみます。
 1.SQL文で解決:エイリアスをダブルクォート「"」で囲んで使用する
 2.Mapから値を取得する際に、JDBCに合わせてキーを大文字小文字変換する
 3.ResultSetを用いて検索する
それぞれの方法について少し考えてみます。

SQL文で解決:エイリアスをダブルクォート「"」で囲んで使用する。
結論からいうと、(DB2は試していないので判別できませんが)期待通り値を取得できました。
この方法の場合、各RDBMSの差異はSQL文のみになり、新規で処理を作成する場合には有効だと思います。
ただし、既存のプログラムを他DBに対応する場合は、SQL文をすべて変更しなければなりません。
また、複数RDBMSに対応する場合、工夫をしなければ各RDBMS用のSQL文を用意することになってしまいそうです。

Mapから値を取得する際に、JDBCに合わせてキーを大文字小文字変換する
一見あまり使いたくない手法ですが、既に複数RDBMS対応を設計に盛り込んでいた場合、その他の場面でも同様の手法で統一的なコーディングにすることができます。
例えばシーケンスやシステム日付を取得する際のSQL文の差異を吸収することと、今回の値を取得する場面で同じ手法が使えます。
具体的にはインターフェースを定義し、各RDBMSに対応したサポートクラスを用意しておきます。
Springフレームワークの中でも各RDBMS間の差異を吸収する手段として、一部にこの方法を用いています。

ResultSetを用いて検索する
java.sql.ResultSetインターフェースは、
 「getter メソッドへの入力として使用される列名では、大文字と小文字は区別されません。」
と明記されています。
実装は各JDBCドライバ開発元に依存しますが、仕様で明記されているので信用することにします。

どの方法も一長一短でしょうし、解決したい内容やスコープが異なるので、それぞれの課題に適した方法を採用すればよいでしょう。
私が遭遇した今回のケースでは、
 ・設計当初から複数RDBMSを想定していたこと
 ・フレームワークを作成しており、JdbcTemplateは使用者からは一切意識する必要がない
ため、解決策2の方法を採用しています。
続きを読む
posted by しん at 22:49| Comment(0) | Spring | このブログの読者になる | 更新情報をチェックする
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。