時間の経過に伴う現在の状況

JDBC が発明され、そのインターフェイスが標準化されてから 20 年が経過し、この間に多くのことが変わりました。

まず、世界がグローバルになり、1 つのサーバーで世界中のユーザーにサービスを提供できるようになりました。インターネットの速度が上がりました。したがって、時間を処理するために別のデータ型が SQL に追加されました。型は次のようになります。

  • DATE - 日付 (年、月、日) を保存します。
  • TIME - 時間、分、秒を保存します。
  • TIMESTAMP - 特定の時点 (日付、時刻、ミリ秒) を保存します。
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP とタイムゾーン (ゾーン名またはオフセット)。

次に、Java はグローバルな時間管理のために DateTime API を導入しました。次のクラスがあります。

  • 日時
    • ローカル日付
    • 現地時間
  • 正確な瞬間:
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • タイムゾーンのある時間:
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

3 番目の興味深い点は、多くの SQL クライアントが、すでにローカル ゾーンにあるサーバーから時刻を受け取りたいと考えていることです。もちろん、その場で時間を変換することもできますが、不便ですし、間違いもあります。

たとえば、今日のすべてのタスクをデータベースから取得したいとします。SQL Server には、このためのCURDATE()関数があります。ここだけサーバーはアメリカにあり、私は日本にいます。そして、「彼の今日」ではなく、「私の今日」の記録をすべて返してほしいと思います。

一般に、SQL サーバーは、異なるタイム ゾーンのクライアントとスマートに連携できなければなりません。

現代の問題には最新の解決策が必要です

原則として、Java DateTime API の新しい型と SQL の型は簡単にマッピングできます。Java でDATEを表すには、 JDK 8 DateTime API のjava.time.LocalDateクラスを使用する必要があります。

データベースの TIME 型は、Java のjava.time.LocalTimejava.time.OffsetTimeの 2 つの型で表すことができます。何も複雑なことはありません。

データベース内のTIMESTAMP型で表される特定の時点は、Java では次の 4 つの型で表すことができます。

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

最後に、TIMESTAMP WITH TIME ZONE は2 つのタイプで表すことができます。

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

DateTime API についてはすでによく知っているので、この点を覚えるのは難しくありません :)

分かりやすいように表形式で書いてみます。

SQLの種類 Java タイプ
日にち java.time.LocalDate
時間 java.time.LocalTime
java.time.OffsetTime
タイムスタンプ java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
タイムゾーン付きのタイムスタンプ java.time.OffsetDateTime
_

日付の取得

良いお知らせがあります。久しぶりに。java.sql Date 型を返すgetDate()メソッドの制限を回避できます。

ポイントはそのオブジェクトです結果セットgetObject()という別の興味深いメソッドがあります。このメソッドは、列と型の 2 つのパラメーターを受け取り、指定された型に変換された列の値を返します。メソッドの一般的な形式は次のとおりです。

ClassName Name = getObject(column, ClassName);

DATE型をjava.time.LocalDate型に変換したい場合は、次のように記述する必要があります。

LocalDate localDate = results.getObject(4, LocalDate.class);

一般に、TIMESTAMP は多数の型に変換できます。

java.time.Instant instant = results.getObject(9, java.time.Instant.class);
java.time.LocalDateTime local = results.getObject(9, java.time. LocalDateTime.class);
java.time.OffsetDateTime offset = results.getObject(9, java.time. OffsetDateTime.class);
java.time.ZonedDateTime zoned = results.getObject(9, java.time. ZonedDateTime.class);

重要! 古い MySQL JDBC Driver を使用している場合、このコードは機能しません。pom.xml に書き込まれた「mysql-connector-java」のバージョン、またはプロジェクト設定のライブラリに追加された「mysql-connector-java」のバージョンに注意してください。

ちなみに、プリミティブ型に null を格納できないことも同様の方法で回避できます。テーブルの列が INT 型の場合、そこから null を取得する方法がいくつかあります。以下の例を参照してください。

Integer id1 = results.getObject(8, Integer.class);    	 // this will work
Integer id2 = results.getObject(8, int.class);                 //this will also work
int id3 = results.getObject(8,  Integer.class);            	//method will return null, JVM will throw NPE
int id4 = results.getObject(8,  int.class);                    	//method will return null, JVM will throw NPE

MySQLのタイムゾーン設定

MySQL でも多くの興味深いことが起こりました。ご存知のとおり、MySQL 接続を作成するときは、それにさまざまなパラメータを追加できます。
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

そのため、MySQL でタイム ゾーンを操作するために 3 つのパラメータが追加されました。サーバーへの接続を確立するときに、これらのパラメータを渡すことができます。

以下にそれらをまとめた表を示します。

パラメータ 価値観 デフォルト値
接続タイムゾーン ローカル | サーバー | ユーザーゾーン サーバ
ForceConnectionTimeZoneToSession 本当 | 間違い 真実
インスタントを保存する 本当 | 間違い 間違い

connectionTimeZoneパラメーターを使用して、すべてのリクエストが実行されるタイムゾーン (タイムゾーン) を選択します。クライアントの観点からは、サーバーは指定されたタイムゾーンで実行されています。

ForceConnectionTimeZoneToSessionパラメータにより、セッション time_zone 変数が無視され、connectionTimeZone に置き換えられます。

最後に、preserveInstantsパラメータは、JVM の timeZone と connectionTimeZone の間の正確な時刻変換を制御します。

最も一般的な構成は次のとおりです。

  • connectionTimeZone=LOCAL &forceConnectionTimeZoneToSession=false - useLegacyDatetimeCode=true の古い MySQL JDBC ドライバー バージョン 5.1 に対応します。

  • connectionTimeZone=LOCAL &forceConnectionTimeZoneToSession=true は、日付と時刻の値を処理する最も自然な方法を提供する新しいモードです。

  • connectionTimeZone=SERVER & prepareInstants=true - useLegacyDatetimeCode=false の古い MySQL JDBC ドライバー バージョン 5.1 に対応します。

  • connectionTimeZone=user_dependent & prepareInstants=true - サーバーのタイム ゾーンが CET/CEST などの一般的な略語として設定されているためにコネクタで認識できない状況を克服するのに役立ちます。

確かに、日付は興味深いトピックですが、多くの問題もあります。よく言われるように、もちろん怖いですが、私は怒っていません。:)