web-dev-qa-db-ja.com

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

私は現在、JavaのiBATISを使用したOracle SQLDATE変換の問題に取り組んでいます。

OracleJDBCシンドライバ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

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

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"/>

ただし、試行されたすべてのマッピングは、同じ切り捨てられた日付値を生成します。それは、iBATISがデータに触れる前に、JDBCがデータの精度を失うというダメージをすでに与えているかのようです。

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

13
RogerV

完全な情報(およびここで説明されているよりも複雑で、使用されているOracleドライバーの特定のバージョンによって異なる場合があります)は、RichardYeeの回答にあります-[Nabbleへの期限切れのリンク]


ナブルから期限切れになる前にすばやくつかむ...

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

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

9.2より前のバージョンでは、OracleJDBCドライバーはDATESQLタイプをJava.sql.Timestampにマップしていました。 Oracle DATE SQLタイプには、Java.sql.Timestampと同様に日付と時刻の両方の情報が含まれているため、これはある程度意味があります。 Java.sql.Dateには時間情報が含まれていないため、Java.sql.Dateへのより明白なマッピングには多少問題がありました。 RDBMSがTIMESTAMPSQLタイプをサポートしていない場合もあったため、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は、この問題を修正します。このリリース以降、ドライバーはデフォルトでSQLDATE列をJava.sql.Timestampにマップします。正しいマッピングを取得するためにV8Compatibleを設定する必要はありません。 V8Compatibleは大幅に非推奨になりました。まったく使用しないでください。 trueに設定しても何も害はありませんが、使用を中止する必要があります。

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

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

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

可能であれば、列タイプをDATEではなくTIMESTAMPに変更する必要があります。

-リチャード


Roger Vossは次のように書いています:私はstackoverflowに次の質問/問題を投稿したので、誰かが解決策を知っているなら、そこで答えられるのを見るのは良いことです:

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

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

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

OracleJDBCシンドライバ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(?、?、?、?、?、?、?、?、?、?))from_dateによる注文

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

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

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

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

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

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

IBATISがマッピングしているのはこれです:

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

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

私も試しました:

または

ただし、試行されたすべてのマッピングは、同じ切り捨てられた日付値を生成します。それは、iBATISがデータに触れる前に、JDBCがデータの精度を失うというダメージをすでに与えているかのようです。

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

8
Gwyn Evans

私はこの問題を解決する方法を見つけました。 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"/>
6
RogerV

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

•問題:

•解決済み:

よろしく。

ペドロ

3
Pedro Calvo

問題は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"/>
1
bob

Richard Yeeは、Oracleの最新のドライバが問題を修正すると述べています。確認できます。ここでも10.2ドライバーで同じ問題が発生し、本日ojdbc5.jar(11.2.0.1.0)にアップグレードされましたが、問題は解消されました。

0
wallenborn

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

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

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

うーん、Oracle PL/SQLの人々は、DATEを広範囲に使用して、秒単位の解像度に依存する値を保持します。 iBATISには、Java.sql.Dateを介してDATEを解釈する代わりに、Javadocがミリ秒の解決を許可すると定義している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に変換し、時刻の精度を失ったようです。

0
RogerV

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

0
ninesided