Oracle JDBC Driver 10で確認。
必ず空文字””が返される。
「JDBC 開発者ガイドおよびリファレンス」に載ってるらしい。
- JDBC APIではPreparedStatementインタフェースを用いてパラメタライズドクエリを実行する
- OracleJDBCドライバではOraclePreparedStatementクラスを用いてパラメタライズドクエリを実行する
- OraclePreparedStatementのsetStringメソッドおよびsetCharメソッドは空白埋めを行わない
- OraclePreparedStatementのsetFixedCharメソッドは空白埋めを行う
- JDBCのPreparedStatementインタフェースにはsetCharおよびsetFixedCharメソッドは定義されていない
(これらはOracleのJDBC拡張機能を用いる事で実装できる)
要するに・・・
CHAR(5)型のCOL1列を持ったテーブルTABLE_Aに対して
[sql]SELECT * FROM TABLE_A WHERE COL1=?[/sql]
というSQLに比較文字列をバインドしてを発行する時、setString(1, "ABC")
とするとCOL1='ABC'
という比較がされ、確実に0件。
setString(1, "ABC ")
としなければならない。
OracleParameterdStatement.setFixedChar(int, String)
を使用することにより、空白埋め比較でバインドする事も出来るが、DBに依存したコードとなる。
ちなみにバインドをしないでリテラルで比較するとヒットする。
[sql]SELECT * FROM TABLE_A WHERE COL1=’ABC'[/sql]
これはOracleでは両辺にVARCHAR2型またはNVARCHAR2型が使用されている場合以外は、空白埋めで比較が行われるためである。
そもそも可変文字列の格納にCHAR型を使うのをやめればすむ話。。
参考:
Connection.close
をしても、ResultSet
とStatement
はGCが動くまでメモリに確保されたまま-
Statement.close
で自動的にResultSet.close
は実行される - ResultSet (Java 2 Platform SE 5.0)
- Oracle10gのJDBCは、DATE型項目の場合java.sql.Date型で返却するために時刻が欠落する。
- Oracle9.0.1より前のJDBCではjava.sql.Timestamp型で返却していたため時刻は欠落しない。
- Oracle11gもjava.sql.Timestamp型で返却する仕様をデフォルトとしているため時刻は欠落しない。
-
Oracle10gの場合、oracle.jdbc.V8Compatible=trueを指定することでjava.sql.Timestamp型で返却する動作となる。
参考:
Oracleオブジェクト型からSQL DATEデータ型へのマッピング
Oracle Database 8i以下のバージョンではTIMESTAMPデータがサポートされていませんでしたが、SQL標準への拡張機能として、Oracle DATEデータに時刻コンポーネントが含まれていました。このため、Oracle Database 8i以下のバージョンのJDBCドライバは、oracle.sql.DATEをjava.sql.Timestampにマップして時刻コンポーネントを保持していました。Oracle Database 9.0.1以上ではTIMESTAMPがサポートされ、9iのJDBCドライバがoracle.sql.DATEをjava.sql.Dateにマッピングするようになりました。このマッピングはOracle DATEデータの時刻コンポーネントを切り捨てたため、不正確でした。この問題を克服するために、Oracle Database 11.1では新しいフラグmapDateToTimestampが導入されました。このフラグのデフォルト値はtrueです。これは、デフォルトで、ドライバがoracle.sql.DATEをjava.sql.Timestampに正確にマップして時刻情報を保持することを意味します。不正確でも10gと互換性のあるoracle.sql.DATEからjava.sql.Dateへのマッピングが必要な場合は、mapDateToTimestampフラグの値をfalseに設定します。
注意:
oracle.sql.DATEからjava.sql.Dateへのマッピングの問題を克服するために、Oracle Database 9.2でフラグV8Compatibleが導入されました。このフラグのデフォルト値はfalseで、これはOracle DATEデータのjava.sql.Dateデータへのマッピングが許可されることを意味します。ただし、このフラグの値をtrueに設定することによって、ユーザーはOracle DATEデータの時刻コンポーネントを保持することができました。このフラグは11gでは非推奨です。これは、Oracle Database 8iとの互換性がサポートされなくなったためです。
Oracleからの回答:
QUESTION
======
oracle.jdbc.V8Compatible=trueにすることで getObject()の戻り値の型がDate→Timestamp と変更される以外に動作が変わることがあるか。ANSWER
======
ございません。oracle.jdbc.V8Compatible=true に設定することによる動作の変更点はgetObject()の戻り値の型が Date→Timestamp と変更されるだけでございます。
DBへの型のマッピングは気をつけないとダメだよという話。
予備知識とか
- ANSI SQLのDATE型は日付のみ保持する。
- OracleのDATE型は日付だけでなく時刻まで保持する。(秒未満の精度とタイムゾーンは保持しない)
DATE型@Java
- java.sql.DateクラスはANSI準拠。日付までしか保持しない
- java.util.Dateクラスはミリ秒まで保持する。
- JDBCではjava.sql.DateクラスはDATE型(JDBC)にマッピングされる。
- JDBCではjava.util.DateクラスはDATE、TIME、TIMESTAMP型(JDBC)にマッピングされる。
問題例とか
- JavaでOracleに日付を登録する場合、やり方によっては時刻情報が落ちる。
- java.sql.Dateのコンストラクタはlong型なミリ秒を受け取れるが、残るのは日付のみ。
参考: