16

私は現在、Java から iBATIS を使用して Oracle SQL DATE 変換の問題に取り組んでいます。

Oracle JDBC シン ドライバー ojdbc14 バージョン 10.2.0.4.0 を使用しています。iBATIS バージョン 2.3.2。Java 1.6.0_10-rc2-b32。

この問題は、次の SQL スニペットによって返される DATE 型の列に関係しています。

SELECT *
FROM   TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) order by from_date

パッケージ プロシージャ コールは、TABLE にラップされている ref カーソルを返します。これにより、テーブルに対する選択クエリであるかのように、結果セットを簡単に読み取ることができます。

PL/SQL Developerでは、返される列の1つであるSQL DATE型のFROM_DATEは、時刻までの精度を持っています:

Tue Dec 16 23:59:00 PST 2008

しかし、iBATIS と JDBC を介してこれにアクセスすると、値は現在までの精度しか保持されません。

Tue Dec 16 12:00:00 AM PST 2008

これは、次のように表示するとより明確になります。

になるはずだった:

1229500740000 milliseconds since epoch
Tuesday, December 16, 2008 11:59:00 PM PST

しかし、代わりにこれを取得します:

1229414400000 milliseconds since epoch
Tuesday, December 16, 2008 12:00:00 AM PST
(as instance of class java.sql.Date)

何を試しても、Java JDBC および iBATIS を介して返されるこの DATE 列の完全な精度を公開することはできません。

iBATIS のマッピング元は次のとおりです。

FROM_DATE : 2008-12-03 : class java.sql.Date

現在の iBATIS マッピングは次のとおりです。

<result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>

私も試しました:

<result property="from_date" jdbcType="DATETIME" javaType="java.sql.Date"/>

また

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Timestamp"/>

ただし、試行されたすべてのマッピングは、切り捨てられた同じ Date 値を生成します。あたかも、iBATIS が触れる前に、JDBC がデータの精度を失うという損害をすでに受けているかのようです。

PL/SQL Developerでテスト・スクリプトと同じSQLスニペットを実行している場合は、JDBCとiBATISを使用することでデータの精度が低下していることは明らかです。まったく受け入れられず、非常にイライラし、最終的には非常に恐ろしい.

4

7 に答える 7

9

完全な情報 (ここで説明されているよりも複雑で、使用されている Oracle ドライバーの特定のバージョンに依存する可能性があります) は、Richard Yee の回答 (現在期限切れになっている Nabble へのリンク) にあります。


nabble から有効期限が切れる前にクイック グラブ...

ロジャー、参照: http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#08_01

具体的には: 単純なデータ型 DATE と TIMESTAMP で何が起こっているのでしょうか? このセクションでは、単純なデータ型について説明します。:-)

9.2 より前では、Oracle JDBC ドライバーは DATE SQL タイプを java.sql.Timestamp にマップしていました。Oracle DATE SQL 型には、java.sql.Timestamp と同様に日付と時刻の両方の情報が含まれているため、これにはある程度の意味がありました。java.sql.Date へのより明白なマッピングは、java.sql.Date に時刻情報が含まれていないため、やや問題がありました。また、RDBMS が TIMESTAMP SQL 型をサポートしていなかったため、DATE を Timestamp にマッピングしても問題はありませんでした。

9.2 では、TIMESTAMP サポートが RDBMS に追加されました。DATE と TIMESTAMP の違いは、TIMESTAMP にはナノ秒が含まれ、DATE には含まれないことです。そのため、9.2 以降、DATE は Date にマップされ、TIMESTAMP は Timestamp にマップされます。残念ながら、時間情報を含めるために DATE 値に依存していた場合、問題があります。

この問題に対処するには、いくつかの方法があります。

DATE の代わりに TIMESTAMP を使用するようにテーブルを変更します。これはおそらくめったに可能ではありませんが、可能な場合は最善の解決策です。

アプリケーションを変更して、defineColumnType を使用して列を DATE ではなく TIMESTAMP として定義します。これには問題があります。なぜなら、必要でない限り defineColumnType を使用したくないからです (defineColumnType とは何か、いつ使用する必要があるかを参照してください)。

getObject ではなく getTimestamp を使用するようにアプリケーションを変更します。可能であればこれは良い解決策ですが、多くのアプリケーションには getObject に依存する汎用コードが含まれているため、常に可能であるとは限りません。

V8Compatible 接続プロパティを設定します。これにより、JDBC ドライバーは新しいマッピングではなく古いマッピングを使用するようになります。このフラグは、接続プロパティまたはシステム プロパティとして設定できます。DriverManager.getConnection または OracleDataSource.setConnectionProperties に渡される java.util.Properties オブジェクトに接続プロパティを追加して、接続プロパティを設定します。システム プロパティを設定するには、Java コマンド ラインに -D オプションを含めます。

java -Doracle.jdbc.V8Compatible="true" MyApp Oracle JDBC 11.1 では、この問題が修正されています。このリリース以降、ドライバーはデフォルトで SQL DATE 列を java.sql.Timestamp にマップします。正しいマッピングを取得するために V8Compatible を設定する必要はありません。V8Compatible は非推奨です。絶対に使用しないでください。true に設定しても問題はありませんが、使用を中止する必要があります。

そのように使用されることはほとんどありませんでしたが、V8Compatible は DATE to Date の問題を修正するためではなく、8i データベースとの互換性をサポートするために存在していました。8i (およびそれ以前) のデータベースは、TIMESTAMP タイプをサポートしていませんでした。V8Compatible を設定すると、データベースからの読み取り時に SQL DATE がタイムスタンプにマップされるだけでなく、データベースへの書き込み時にすべてのタイムスタンプが SQL DATE に変換されます。8i はサポートされなくなったため、11.1 JDBC ドライバーはこの互換モードをサポートしません。このため、V8Compatible はサポートされなくなりました。

前述のように、11.1 ドライバーはデフォルトで、データベースから読み取るときに SQL DATE を Timestamp に変換します。これは常に正しいことであり、9i での変更は間違いでした。11.1 ドライバーは正しい動作に戻りました。アプリケーションで V8Compatible を設定していなくても、ほとんどの場合、動作に違いは見られません。getObject を使用して DATE 列を読み取ると、違いに気付く場合があります。結果は、日付ではなくタイムスタンプになります。Timestamp は Date のサブクラスであるため、これは通常問題になりません。違いに気付くのは、DATE から Date への変換に依存して時間コンポーネントを切り捨てた場合、または値に対して toString を実行した場合です。それ以外の場合、変更は透過的でなければなりません。

何らかの理由でアプリがこの変更に非常に敏感で、単に 9i-10g の動作が必要な場合は、設定できる接続プロパティがあります。mapDateToTimestamp を false に設定すると、ドライバーはデフォルトの 9i-10g の動作に戻り、DATE を Date にマップします。

可能であれば、列の型を DATE ではなく TIMESTAMP に変更してください。

-リチャード


Roger Voss は次のように書いています: 私は以下の質問/問題を stackoverflow に投稿しました。

Java JDBC経由でiBATISを使用したOracle SQL DATE変換の問題

問題の説明は次のとおりです。

私は現在、Java から iBATIS を使用して Oracle sql DATE 変換の問題に取り組んでいます。

Oracle JDBC シン ドライバー ojdbc14 バージョン 10.2.0.4.0 を使用しています。iBATIS バージョン 2.3.2。Java 1.6.0_10-rc2-b32。

この問題は、次の SQL スニペットによって返される DATE 型の列に関係しています。

SELECT * FROM TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) 日付順

パッケージ プロシージャ コールは、TABLE にラップされている ref カーソルを返します。これにより、テーブルに対する選択クエリであるかのように、結果セットを簡単に読み取ることができます。

PL/SQL Developerでは、返される列の1つであるSQL DATE型のFROM_DATEは、時刻までの精度を持っています:

Tue Dec 16 23:59:00 PST 2008

しかし、iBATIS と JDBC を介してこれにアクセスすると、値は現在までの精度しか保持されません。

Tue Dec 16 12:00:00 AM PST 2008

これは、次のように表示するとより明確になります。

エポックから 1229500740000 ミリ秒 2008 年 12 月 16 日火曜日午後 11:59:00 PST

しかし、代わりにこれを取得します: エポックから 1229414400000 ミリ秒 2008 年 12 月 16 日火曜日 12:00:00 AM PST (クラス java.sql.Date のインスタンスとして)

何を試しても、Java JDBC および iBATIS を介して返されるこの DATE 列の完全な精度を公開することはできません。

iBATIS のマッピング元は次のとおりです。

FROM_DATE : 2008-12-03 : クラス java.sql.Date

現在の iBATIS マッピングは次のとおりです。

私も試しました:

また

ただし、試行されたすべてのマッピングは、切り捨てられた同じ Date 値を生成します。あたかも、iBATIS が触れる前に、JDBC がデータの精度を失うという損害をすでに受けているかのようです。

明らかに、JDBC と iBATIS を使用することでデータの精度がいくらか失われていますが、これは、テスト スクリプトと同じ SQL スニペットを実行している PL/SQL Developer にとどまっている場合には発生しません。まったく受け入れられず、非常にイライラし、最終的には非常に恐ろしい.

于 2009-01-20T12:50:41.060 に答える
6

私はこの問題を解決する方法を見つけました。iBATISでは、カスタムタイプハンドラーを登録できます。だから私のsqlmap-config.xmlファイルにこれを追加しました:

<typeAlias alias="OracleDateHandler" type="com.tideworks.ms.CustomDateHandler"/>
<typeHandler callback="OracleDateHandler" jdbcType="DATETIME" javaType="date"/>

そして、iBATISTypeHandlerCallbackインターフェースを実装するこのクラスを追加しました。

// corrected getResult()/setParameter() to correctly deal with when value is null
public class CustomDateHandler implements TypeHandlerCallback {
    @Override
    public Object getResult(ResultGetter getter) throws SQLException {
        final Object obj = getter.getTimestamp();
        return obj != null ? (Date) obj : null;
    }

    @Override
    public void setParameter(ParameterSetter setter,Object value) throws SQLException {
        setter.setTimestamp(value != null ? new Timestamp(((Date)value).getTime()) : null);
    }

    @Override
    public Object valueOf(String datetime) {
        return Timestamp.valueOf(datetime);
    }
}

Oracle DATEをマップする必要があるときはいつでも、次のように説明します。

<result property="from_date" jdbcType="DATETIME" javaType="date"/>
于 2008-12-21T08:45:16.330 に答える
3

jdbcType="TIMESTAMP"の代わりに使用して問題を解決しましたjdbcType="DATE"

• 問題:

<result column="MY_UTC_POS" property="myUtcPosition" jdbcType="DATE" />

• 解決済み:

<result column="MY_UTC_POS" property="myUtcPosition" jdbcType="TIMESTAMP" />
于 2009-03-18T12:42:43.850 に答える
1

問題は Oracle ドライバーにあります。

私が見つけた最善の解決策は、すべての jdbcType="DATE" を jdbcType="TIMESTAMP" に変更し、すべての #column_name:DATE# を #column_name:TIMESTAMP# に変更することでした。

だから変更:

<result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Date"/>
于 2009-09-03T22:03:57.333 に答える
0

はい、わかりました。プレーンなSQL DATE標準は、日単位の解決のみを保存する必要があります。実際、これはOracleのDATE型のスニペットです。

Oracleは、SQL2標準とは異なりますが、日付と時刻の両方をサポートします。Oracleは、日付と時刻の2つの別個のエンティティを使用するのではなく、1つのDATEのみを使用します。DATE型は、月、日、年だけでなく、時、分、秒も含む特別な内部形式で格納されます。

これは、OracleのDATEが標準のSQLDATEを超えていることを示しています。

うーん、Oracle PL / SQLの人々は、DATEを広範囲に使用して、秒単位の解像度に依存する値を保持します。iBATISには、java.sql.Dateを介してDATEを解釈する代わりに、javadocsがミリ秒の解決を許可すると定義しているjava.util.Dateをオーバーライドして解釈できる、Hibernatesql方言の概念のようなものが必要なようです。

残念ながら、マッピングを次のようなものに変更した場合:

<result property="from_date" jdbcType="DATE" javaType="java.util.Date"/>

また

<result property="from_date" jdbcType="DATETIME" javaType="java.util.Date"/>

それはまだ最初にSQLDATEをjava.sql.Dateに変換し、時刻の精度を失ったようです。

于 2008-12-21T03:26:43.943 に答える
0

Richard Yee は、Oracle の最新のドライバーがこの問題を解決すると述べています。私はそれを確認することができます。ここで 10.2 ドライバーに同じ問題があり、今日 ojdbc5.jar (11.2.0.1.0) にアップグレードしたところ、問題はなくなりました。

于 2009-10-06T16:55:21.713 に答える
0

問題は、の使用ですjava.sql.DateJavadocによると、インスタンスによってラップされるミリ秒の値java.sql.Dateは、SQL の定義に準拠するために、インスタンスが関連付けられている特定のタイム ゾーンで時、分、秒、およびミリ秒をゼロに設定して「正規化」する必要がありますDATE

于 2008-12-20T23:50:34.503 に答える