>

2010年9月28日火曜日

JavaからJDBCを使用してOracleにSQLを投げるときのTimezoneの注意点

有名な話ではあるとは思うがJavaのOracle JDBCドライバはタイムゾーン情報を持てない。
※JDBC全般の話なのかOracle特有の話なのかは未確認だがAPI仕様を見る限りJDBC全般の話かも。

JavaからJDBCを使用してOracleにSQLを投げるときにSQLのWHERE句に日時が入っているときはタイムゾーンに関して注意が必要。

PreparedStatementにsetTimeStampやsetDateしてSQLを実行する際、JDBCは引数で渡すDateのエポック秒でSQLを実行するのではなく、引数で渡したDataをマシンのデフォルトタイムゾーン
で出力した(タイムゾーンを伴わない)日時でSQLを実行する。DBを受け取った日時をDBのタイムゾーン設定に基づいて解釈する。

なので特に対策をしないで実行すると次のような問題が起こる。
  • クライアントマシンのタイムゾーンとDB(Oracle)のタイムゾーンが異なると意図する結果が戻らない。
  • クライアントマシンのタイムゾーンが一致していたとしてもJavaで勝手にサマータイムとかが考慮されて一時間ずれた結果が戻ったりする。

特に二つ目の問題は、プログラムをリリースした時期が冬で正常に動作していたのにサマータイムの季節に突然発症したりして厄介。

以上を踏まえるとクライアントのJavaプログラムでSQLを呼び出すときには次の処理を忘れずに行う必要がある。
  1. Javaプログラム中でDBのタイムゾーンをGMT-HH形式で保持する。
    Timezone oracleTZ = TimeZone.getTimeZone("GMT-5");
  2. そのタイムゾーンをデフォルトタイムゾーンとして指定する。
    TimeZone.setDefault(oracleTZ);

デフォルトにすると不都合な場合はPreparedStatementで時刻を扱う都度設定。
PreparedStatement ps = ..... ;
ps.setTimestamp( 1, t, Calendar.getInstance(oracleTZ) );


まとめると
  • OracleJDBC(OracleのみならずJDBC全般の可能性あり)はタイムゾーン情報を持たないためJavaプログラム中にデータベースのタイムゾーン設定する必要がある。
  • Javaプログラムでタイムゾーンを設定する場合は"EST"みたいな三文字略称を使うと勝手にサマータイムとかが考慮されることがあるので"GMT-5"のような形式で設定するのがよい。

捕捉
もっとも大抵の場合はWHERE句でバインド変数の日時を使うのを避けてOracleのSYSDATEなどDB内で完結した表現で日時を設定するのが無難と言えば無難。

0 件のコメント: