java - 対応表 - oracle jdbcドライバ インストール




Java 8でJDBCの日付処理を修正する機会を逃した? (2)

2015-09-13の日付をデータベースで表現するには、タイムゾーンを選択し、そのタイムゾーンの文字列 "2015-09-13T00:00:00.000"をjava.util.Dateとして解析し、ミリ秒JDBCドライバが日付を正しく再計算できるように、選択したタイムゾーンを保持するカレンダを渡して、準備された文でsetDate()を呼び出します。このミリ秒の値から-13

どうして? ちょうど電話する

Date.valueOf("2015-09-13"); // From String
Date.valueOf(localDate);    // From java.time.LocalDate

この動作は、すべてのJDBCドライバで正しいものになります。タイムゾーンのないローカル日付。 逆の操作は次のとおりです。

date.toString();    // To String
date.toLocalDate(); // To java.time.LocalDate

java.util.Dateに対するjava.sql.Dateの依存性に決して依存するべきではなく、したがって、 Date(long)またはDate.getTime()介してjava.time.Instantのセマンティクスを継承するという事実は、

ですから、下位互換性を維持しながらJDBCの混乱を取り除く巨大な機会を逃したことはありませんか? [...]

場合によります。 JDBC 4.2仕様では、 setObject(int, localDate)を介してLocalDate型をバインドし、 getObject(int, LocalDate.class)を介してLocalDate型をフェッチすることができるように指定しています。 あなたが提案したように、もっとフォーマルなdefaultメソッドほどエレガントではありません。

Java 8 + JDBCの専門家は、次のような理由で何か問題があるかどうか教えていただけますか? そして、もし神の秘密にあれば、なぜこれが行われていないのでしょうか?

java.sql.Dateは、現在、JDBCがDATE SQL型にマップするために使用する型です。これは、時間のない日付を表し、タイムゾーンはありません。 しかし、実際にはjava.util.Dateサブクラスなので、このクラスは非常に設計されています。これはミリ秒までの正確な瞬間を保存します。

2015-09-13の日付をデータベースで表現するには、タイムゾーンを選択し、そのタイムゾーンの文字列 "2015-09-13T00:00:00.000"をjava.util.Dateとして解析し、ミリ秒JDBCドライバが日付を正しく再計算できるように、選択したタイムゾーンを保持するカレンダを渡して、準備された文でsetDate()を呼び出します。このミリ秒の値から-13。 このプロセスは、カレンダーを渡すのではなく、どこにでもデフォルトのタイムゾーンを使用することで少し簡単になります。

Java 8にはLocalDateクラスが導入されています。これは、DATEデータベース型の方がはるかに適しています。正確な時刻ではないため、タイムゾーンに依存しません。 また、Java 8では、PreparedStatementインタフェースとResultSetインタフェースの下位互換性のある変更を可能にするデフォルトのメソッドも導入されています。

ですから、下位互換性を維持しながらJDBCの混乱を取り除く巨大な機会を逃したことはありませんか? Java 8では、PreparedStatementとResultSetにこれらのデフォルトメソッドを追加するだけでした。

default public void setLocalDate(int parameterIndex, LocalDate localDate) {
    if (localDate == null) {
        setDate(parameterIndex, null);
    }
    else {
        ZoneId utc = ZoneId.of("UTC");
        java.util.Date utilDate = java.util.Date.from(localDate.atStartOfDay(utc).toInstant());
        Date sqlDate = new Date(utilDate.getTime());
        setDate(parameterIndex, sqlDate, Calendar.getInstance(TimeZone.getTimeZone(utc)));
    }
}

default LocalDate getLocalDate(int parameterIndex) {
    ZoneId utc = ZoneId.of("UTC");
    Date sqlDate = getDate(parameterIndex, Calendar.getInstance(TimeZone.getTimeZone(utc)));
    if (sqlDate == null) {
        return null;
    }

    java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
    return utilDate.toInstant().atZone(utc).toLocalDate();
}

もちろん、TIMESTAMP型のInstantのサポートとTIME型のLocalTimeのサポートには同じ理由があります。


短い答え:いいえ、Java 8 / JDBC 4.2 ではJava 8の日付と時刻の型をサポートしているため、日時の処理が修正されています 。 これはデフォルトの方法ではエミュレートできません。

Java 8では、PreparedStatementとResultSetにこれらのデフォルトメソッドを追加するだけでした。

これはいくつかの理由で不可能です:

  • java.sql java.time.OffsetDateTimeが同等でない
  • java.time.LocalDateTimeは、 java.sql.Timestampして変換するときにDSTの遷移を中断しjava.sql.Timestampこれは、後者がJVMのタイムゾーンに依存するためです(下のコードを参照)。
  • java.sql.Time分解能はミリ秒ですが、 java.time.LocalTime分解能はナノ秒です

したがって、適切なドライバのサポートが必要です。デフォルトのメソッドは、データ損失を招くjava.sql型を介して変換する必要があります。

JVMのタイムゾーンでこのコードを実行すると、夏時間になると破損します。 次のトランジションを検索し、トランジションのLocalDateTimeあるLocalDateTimeLocalDateTimeします。 Java LocalDateTimeまたはSQL TIMESTAMPはタイムゾーンがないため、タイムゾーン規則がなく、したがって夏時間がないため、これは完全に有効です。 一方、 java.sql.TimestampはJVMタイムゾーンにバインドされているため、夏時間の影響を受けます。

ZoneId systemTimezone = ZoneId.systemDefault();
Instant now = Instant.now();

ZoneRules rules = systemTimezone.getRules();
ZoneOffsetTransition transition = rules.nextTransition(now);
assertNotNull(transition);
if (!transition.getDateTimeBefore().isBefore(transition.getDateTimeAfter())) {
  transition = rules.nextTransition(transition.getInstant().plusSeconds(1L));
  assertNotNull(transition);
}

Duration gap = Duration.between(transition.getDateTimeBefore(), transition.getDateTimeAfter());
LocalDateTime betweenTransitions = transition.getDateTimeBefore().plus(gap.dividedBy(2L));

Timestamp timestamp = java.sql.Timestamp.valueOf(betweenTransitions);
assertEquals(betweenTransitions, timestamp.toLocalDateTime());




java-8