web-dev-qa-db-ja.com

JDBC ResultSet:getDateTimeが必要ですが、getDateとgetTimeStampしかありません

JDBCを使用してOracle DBテーブルからDATETIME列を取得したいと思います。ここに私のコードがあります:

int columnType = rsmd.getColumnType(i);
if(columnType == Types.DATE)
{
    Date aDate = rs.getDate(i);
    valueToInsert = aDate.toString();
}
else if(columnType == Types.TIMESTAMP)
{
    Timestamp aTimeStamp = rs.getTimestamp(i);
    valueToInsert = aTimeStamp.toString();
}
else
{
    valueToInsert = rs.getString(i);
}

最初に列タイプを識別する必要があります。興味のあるフィールドは、Types.DATEとして認識されますが、この形式は「07.05.2009 13:49:32」であるため、DBでは実際にはDATETIMEです。

getDateは時刻を切り捨てます: "07.05.2009"およびgetStringは ".0"を追加します: "07.05.2009 13:49:32.0"

もちろん、最後の.0を削除して常にgetStringを操作することもできますが、これは汚い回避策です。

何か案は ? getDateTimeメソッドを探していました。

乾杯、ティム

43
Tim
Java.util.Date date;
Timestamp timestamp = resultSet.getTimestamp(i);
if (timestamp != null)
    date = new Java.util.Date(timestamp.getTime()));

次に、好きなようにフォーマットします。

86
Leos Literak

Leos Literakによる回答 は正しいですが、今では時代遅れで、面倒な古い日時クラスの1つであるJava.sql.Timestampを使用しています。

tl; dr

それは本当にDBのDATETIMEです

いいえ、そうではありません。 OracleデータベースにはDATETIMEなどのデータ型はありません。

GetDateTimeメソッドを探していました。

質問に見られる面倒なレガシークラスではなく、JDBC 4.2以降のJava.timeクラスを使用します。特に、Java.sql.TIMESTAMPではなく、SQL標準型TIMESTAMP WITH TIME ZONEなどのちょっとしたInstantクラスを使用します。

工夫されたコードスニペット:

if( 
    JDBCType.valueOf( 
        myResultSetMetaData.getColumnType( … )
    )
    .equals( JDBCType.TIMESTAMP_WITH_TIMEZONE ) 
) {
    Instant instant = myResultSet.getObject( … , Instant.class ) ;
}

詳細

JDBCを使用してOracle DBテーブルからDATETIME列を取得したいと思います。

this doc によると、Oracleデータベースには列データ型DATETIMEがありません。その用語は、すべての日時タイプをグループとして参照するOracleの単語のようです。

型を検出し、どのデータ型で分岐するかというコードのポイントはわかりません。一般に、特定のテーブルと特定のビジネス上の問題のコンテキストでコードを明示的に作成する必要があると思います。おそらく、これは何らかの汎用フレームワークで役立つでしょう。主張する場合は、さまざまなタイプについて学び、Java 8以降に組み込まれている非常に有用な新しいJava.timeクラスについて学習してください。質問で使用されるクラス。

ダムストリングではなくスマートオブジェクト

valueToInsert = aDate.toString();

Stringオブジェクトとして、データベースとテキストで日時値を交換しようとしているようです。しないでください。

データベースと日時値を交換するには、日時オブジェクトを使用します。 Java 8以降では、以下に説明するように、Java.timeオブジェクトを意味します。

さまざまな型システム

日付と時刻に関連する3種類のデータタイプを混同している可能性があります。

  • 標準SQLタイプ
  • 独自のタイプ
  • JDBCタイプ

SQL標準タイプ

SQL標準 5つのタイプを定義します

  • DATE
  • TIME WITHOUT TIME ZONE
  • TIME WITH TIME ZONE
  • TIMESTAMP WITHOUT TIME ZONE
  • TIMESTAMP WITH TIME ZONE

日付のみ

  • DATE
    日付のみ、時間なし、タイムゾーンなし。

時刻のみ

  • TIMEまたはTIME WITHOUT TIME ZONE
    時間のみ、日付なし。入力の一部として指定されたタイムゾーンを黙って無視します。
  • TIME WITH TIME ZONE(またはTIMETZ
    時間のみ、日付なし。入力に十分なデータが含まれている場合、タイムゾーンと夏時間規則を適用します。 Postgresのドキュメントで説明されています のように、他のデータ型が与えられた場合、疑わしい有用性があります。

日付と時刻

  • TIMESTAMPまたはTIMESTAMP WITHOUT TIME ZONE
    日付と時刻、ただしタイムゾーンは無視されます。データベースに渡されるタイムゾーン情報はすべて無視され、UTCへの調整は行われません。したがって、このnotはタイムライン上の特定の瞬間を表しますが、約26〜27時間にわたる可能性のある瞬間の範囲を表します。タイムゾーンまたはオフセットが(a)不明、または(b)「世界中のすべての工場が正午に昼休みに閉まる」などの無関係な場合に使用します。 疑問がある場合は、正しいタイプではない可能性が高い
  • TIMESTAMP WITH TIME ZONE(またはTIMESTAMPTZ
    タイムゾーンに関する日付と時刻。この名前は実装に応じて誤った名前になっていることに注意してください。一部のシステムは、指定されたタイムゾーン情報を保存する場合があります。 Postgres などの他のシステムでは、タイムゾーン情報はnotstoredであり、代わりにデータベースに渡されるタイムゾーン情報が調整に使用されますdate-timeからUTC。

専有

多くのデータベースは、独自の日時関連タイプを提供します。独自のタイプは、widelyによって異なります。いくつかは避けるべき古い古いタイプです。一部のベンダーは、特定の利点を提供すると信じています。標準タイプのみを使用するかどうかを決定します。注意:一部の独自型には、標準型と競合する名前があります。私はあなたを見ています Oracle DATE

JDBC

Javaプラットフォームは、SQL標準データベースまたは特定のデータベースとは異なる方法で日時の内部詳細を処理します。 JDBCドライバー の仕事は、これらの違いを仲介し、橋として機能し、必要に応じて型と実際に実装されたデータ値を変換することです。 Java.sql。*パッケージ はそのブリッジです。

JDBCレガシークラス

Java 8より前は、 JDBC 仕様が日時作業用に3つのタイプを定義していました。最初の2つは、バージョン8以前のようにハッキングです。Javaには、日付のみまたは時間のみの値を表すクラスがありませんでした。

  • Java.sql.Date
    日付のみをシミュレートし、時間がない、時間帯がないふりをします。このクラスは、日付andの両方を追跡するJava.util.Dateのラッパーであるため、混乱する可能性があります。内部的に、時間部分はゼロ(UTC真夜中)に設定されます。
  • Java.sql.Time
    時間のみ、日付がない、およびタイムゾーンがないふりをします。このクラスも、日付andの両方を追跡するJava.util.Dateの薄いラッパーであるため、混乱を招く可能性があります。内部的には、日付はゼロに設定されています(1970年1月1日)。
  • Java.sql.TimeStamp
    日付と時刻、ただしタイムゾーンはありません。これもJava.util.Dateの薄いラッパーです。

したがって、 ResultSet interface には「getDateTime」メソッドがないという質問に答えます。このインターフェイスは、JDBCで定義されている3つのブリッジングデータタイプに対して getterメソッド を提供します。

最初のものには、タイムゾーンまたはUTCからのオフセットの概念がないことに注意してください。最後のJava.sql.Timestampは、そのtoStringメソッドが示していることにもかかわらず、常にUTCです。

JDBCモダンクラス

上記の設計が不十分なJDBCクラスは避けてください。それらはJava.timeタイプに取って代わられます。

  • Java.sql.Dateの代わりに、LocalDateを使用します。 SQL標準のDATEタイプに適合します。
  • Java.sql.Timeの代わりに、LocalTimeを使用します。 SQL標準のTIME WITHOUT TIME ZONE型に適合します。
  • Java.sql.Timestampの代わりに、Instantを使用します。 SQL標準のTIMESTAMP WITH TIME ZONE型に適合します。

JDBC 4.2以降では、Java.timeオブジェクトをデータベースと直接交換できます。 setObject/getObjectメソッドを使用します。

挿入/更新。

myPreparedStatement.setObject( … , instant ) ;

検索。

Instant instant = myResultSet.getObject( … , Instant.class ) ;

Instant クラスは、 UTC のタイムライン上の瞬間を ナノ秒 (小数部の最大9桁)の解像度で表します。

UTCとしてではなく、特定の地域(タイムゾーン)の人々が使用する壁時計時間で見たInstantの瞬間を確認したい場合は、ZoneIdを適用してZonedDateTimeオブジェクトを取得して調整します。

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

結果のZonedDateTimeオブジェクトは同じ瞬間、タイムライン上の同じ同時ポイントです。新しい日は東の方が早いため、日付と時刻は異なります。たとえば、ニュージーランドの真夜中から数分後は、UTCでは「昨日」のままです。

InstantまたはZonedDateTimeのいずれかにさらに別のタイムゾーンを適用して、他の地域の人々が使用するさらに別の壁時計時刻を介して同じ瞬間を見ることができます。

ZoneId zMontréal = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdtMontréal = zdtAuckland.withZoneSameInstant( zMontréal ) ;  // Or, for the same effect: instant.atZone( zMontréal ) 

したがって、3つのオブジェクト(instantzdtAucklandzMontréal)があり、すべてが同じ瞬間、タイムライン上の同じポイントを表します。

検出タイプ

データベースのデータ型の検出に関する質問のコードに戻るには、(a)私の専門分野ではなく、(b)上で述べたようにこれを避けます。(c)これを主張する場合は、注意してくださいJava 8以降では、Java.sql.Typesクラスは旧式です。そのクラスは、新しいインターフェイスを実装する Enum の適切な Java JDBCType に置き換えられました SQLType 。関連する質問の この回答 を参照してください。

この変更は、 JDBC Maintenance Release 4.2 、セクション3および4にリストされています。

Java.sql.JDBCType列挙型の追加

JDBCタイプと呼ばれる汎用SQLタイプを識別するために使用されるEnum。その目的は、Types.Javaで定義されている定数の代わりにJDBCTypeを使用することです。

列挙型は古いクラスと同じ値を持ちますが、 type-safety を提供します。

構文に関する注意:最新のJavaでは、switchオブジェクトでEnumを使用できます。したがって、質問にあるように、カスケードif-thenステートメントを使用する必要はありません。難点の1つは、なんらかの技術的な理由で切り替える場合、enumオブジェクトの名前を非修飾で使用する必要があるため、修飾されたTIMESTAMP_WITH_TIMEZONEではなくJDBCType.TIMESTAMP_WITH_TIMEZONEswitchを実行する必要があることです。 static importステートメントを使用します。

そのため、次のコード例のようなことができると思います(まだ試していません)。

final int columnType = myResultSetMetaData.getColumnType( … ) ;
final JDBCType jdbcType = JDBCType.valueOf( columnType ) ;

switch( jdbcType ) {  

    case DATE :  // FYI: Qualified type name `JDBCType.DATE` not allowed in a switch, because of an obscure technical issue. Use a `static import` statement.
        …
        break ;

    case TIMESTAMP_WITH_TIMEZONE :
        …
        break ;

    default :
        … 
        break ;

}

Java.timeについて

Java.time フレームワークは、Java 8以降に組み込まれています。これらのクラスは、 Java.util.DateCalendar 、& SimpleDateFormat などの厄介な古い legacy date-timeクラスに取って代わります。

Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time クラスへの移行を推奨しています。

詳細については、 Oracle Tutorial を参照してください。また、Stack Overflowで多くの例と説明を検索してください。仕様は JSR 31 です。

Java.timeオブジェクトをデータベースと直接交換できます。 JDBC 4.2 以降に準拠する JDBCドライバー を使用します。文字列もJava.sql.*クラスも必要ありません。

Java.timeクラスはどこで入手できますか?

  • Java SE 8Java SE 9Java SE 1 以降
    • ビルトイン。
    • 実装がバンドルされた標準Java APIの一部。
    • Java 9は、いくつかのマイナーな機能と修正を追加します。
  • Java SE 6 および Java SE 7
    • Java.time機能の多くは、 ThreeTen-Backport のJava 6および7にバックポートされています。
  • Android
    • Androidの以降のバージョンには、Java.timeクラスの実装がバンドルされています。
    • 以前のAndroid(<26)の場合、 ThreeTenABP プロジェクトはThreeTen-Backportを適応させます (上記の通り)。 ThreeTenABPの使用方法… を参照してください。

ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。 IntervalYearWeekYearQuartermore などの便利なクラスがあります。


PDATE:Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time への移行を推奨していますクラス。このセクションは履歴としてそのまま残りました。

ジョーダタイム

Java 8(Java.time。*パッケージ)より前は、Java(Java.util.Date&Calendar、Java.text.SimpleDateFormat)にバンドルされている日時クラスは、面倒で混乱しやすいため、欠陥がある。

より良い方法は、JDBCドライバーが提供するものを使用して、Joda-Timeオブジェクトを作成するか、Java 8 Java.time。* package で作成することです。最終的に、新しいJava.time。*クラスを自動的に使用する新しいJDBCドライバーが表示されるはずです。それまで、Java.sql.Timestampなどのクラスに、 toInstantfromInstantなどのJava.timeを挿入するためのメソッドがいくつか追加されました。

ひも

質問の後半については、文字列のレンダリング…フォーマッタオブジェクトを使用して文字列値を生成する必要があります。

昔ながらの方法は、Java.text.SimpleDateFormatを使用することです。推奨されません。

Joda-Timeはさまざまな組み込みフォーマッタを提供しますが、独自のフォーマッタを定義することもできます。しかし、あなたが述べたようにログやレポートを書く場合、最良の選択はISO 8601形式かもしれません。その形式は、たまたま Joda-Time とJava.timeによって使用されるデフォルトです。

サンプルコード

//Java.sql.Timestamp timestamp = resultSet.getTimestamp(i);
// Or, fake it 
// long m = DateTime.now().getMillis();
// Java.sql.Timestamp timestamp = new Java.sql.Timestamp( m );

//DateTime dateTimeUtc = new DateTime( timestamp.getTime(), DateTimeZone.UTC );
DateTime dateTimeUtc = new DateTime( DateTimeZone.UTC ); // Defaults to now, this moment.

// Convert as needed for presentation to user in local time zone.
DateTimeZone timeZone = DateTimeZone.forID("Europe/Paris");
DateTime dateTimeZoned = dateTimeUtc.toDateTime( timeZone );

コンソールにダンプ…

System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeZoned: " + dateTimeZoned );

実行すると…

dateTimeUtc: 2014-01-16T22:48:46.840Z
dateTimeZoned: 2014-01-16T23:48:46.840+01:00
40
Basil Bourque

これは働いた:

    Date date = null;
    String dateStr = rs.getString("doc_date");
    if (dateStr != null) {
        date = dateFormat.parse(dateStr);
    }

simpleDateFormatを使用します。

0

私が選んだ解決策は、mysqlクエリで日付をフォーマットすることでした:

String l_mysqlQuery = "SELECT DATE_FORMAT(time, '%Y-%m-%d %H:%i:%s') FROM uld_departure;"
l_importedTable = fStatement.executeQuery( l_mysqlQuery );
System.out.println(l_importedTable.getString( timeIndex));

私はまったく同じ問題を抱えていました。 mysqlテーブルには次のような形式の日付が含まれていますが:2017-01-01 21:02:50

String l_mysqlQuery = "SELECT time FROM uld_departure;"
l_importedTable = fStatement.executeQuery( l_mysqlQuery );
System.out.println(l_importedTable.getString( timeIndex));

そのようにフォーマットされた日付を返していました:2017-01-01 21:02:50.0

0
Albizia