web-dev-qa-db-ja.com

Java 8データベースのLocalDateとLocalDateTimeを使用したHibernate

私の要件は、すべての日付と日時をUTCタイムゾーンでデータベースに保存することです。 HibernateエンティティでJava 8のLocalDateLocalDateTimeを使用しています。

LocalDateLocalDateTimeにはタイムゾーンが関連付けられていないので、それは正しいですか?

そうでない場合は、古き良き(またはレガシー)DateTimestampを使用してフォールバックする必要がありますか?

またはJava 8のInstant?を使用する必要がありますか?Instantを使用する場合、時間なしで日付部分のみを保存する可能性はありますか?

データベースはMySQLとSQL Serverであり、これはSpring Bootアプリケーションです。

17
adi

「ローカル…」タイプには、意図的にタイムゾーンの概念がありません。そのため、notはタイムライン上の瞬間を表します。 LocalDateTimeは、あいまいな範囲のモーメントを表しますが、オフセットまたはタイムゾーンを割り当てるまで、実際の意味はありません。つまり、ZoneIdを適用してZonedDateTimeを取得します。

たとえば、今年のクリスマスは12月25日の最初の瞬間から始まると言うには、次のように言います。

LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );

しかし、真夜中のそのストロークは、西よりも東で早く起こります。

エルフの物流部門が、UTCの14時間先の世界で最も早いタイムゾーンである太平洋の Kiribati から始まるサンタのルートをマップする理由です。そこに配達した後、彼らはサンタを西の方へニュージーランドのような場所にその深夜にルーティングします。それから彼らの真夜中のためにアジアに後で。その後、インドなどが、数時間後に彼らの真夜中にヨーロッパに到着し、それから数時間後に彼らの真夜中に北アメリカの東海岸に到着しました。これらすべての場所で、sameLocalDateTimeが異なる瞬間に発生し、各配信はdifferentZonedDateTimeオブジェクト。

そう…

  • 25日の午前0時以降に始まるクリスマスのconceptを記録する場合は、LocalDateTimeを使用して、TIMESTAMP WITHOUT TIME ZONE型のデータベース列に書き込みます。
  • サンタが行う各配達の正確な瞬間を記録したい場合は、ZonedDateTimeを使用して、タイプTIMESTAMP WITH TIME ZONEのデータベース列に書き込みます。

2番目の箇条書きについては、ほぼすべてのデータベースシステムがゾーン情報を使用して日時をUTCに調整し、そのUTC値を格納することに注意してください。ゾーン情報を保存するものもありますが、Postgresなどは、UTCへの調整に使用した後にゾーン情報を破棄するものもあります。そのため、「タイムゾーンを使用する」は誤った呼び名で、実際には「タイムゾーンを使用するrespect」を意味します。元のゾーンを覚える必要がある場合は、その名前を別の列と一緒に保存する必要があります。

Local…タイプを使用するもう1つの理由は、将来の予定のためです。政治家は、管轄のタイムゾーンを頻繁に変更することを楽しんでいます。彼らは夏時間(DST)を採用することを好みます。 DSTカットオーバーの日付を変更したい。彼らはDSTの採用をやめるのが好きです。彼らはタイムゾーンを再定義して、境界を変えたいと思っています。彼らは、15分などの量で、UTCからのオフセットを再定義することを好みます。また、事前に通知することはほとんどなく、わずか1、2か月の警告でこのような変更を行います。

そのため、来年または6か月以内に健康診断の予約をするために、時間帯の定義を予測することはできません。したがって、午前9時の予定が必要な場合は、TIMESTAMP WITHOUT TIME ZONE型のデータベース列に記録されているLocalTimeまたはLocalDateTimeを使用する必要があります。それ以外の場合、その午前9時の予定は、DSTカットオーバーが延期されるゾーンにある場合、午前8時または午前10時と表示されることがあります。

予測スケジュールを生成するとき、タイムゾーン(ZoneId)をそれらの「ローカル」(ゾーン化されていない)値に適用して、ZonedDateTimeオブジェクトを作成できます。しかし、政治家がゾーンを変更することによって意味を台無しにするかもしれないとき、あまりにも遠くにあるものに頼らないでください。

ヒント:DSTおよびタイムゾーンに対するこれらの頻繁な変更は、タイムゾーンを維持する必要があることを意味します tzdata データベースを最新の状態に保ちます。ホストOS、JVM、およびおそらくPostgresなどのデータベースシステムにtzdataがあります。 3つすべてを頻繁に更新する必要があります。トルコは昨年、数週間前の通知でDSTにとどまることを決定したなど、これらの製品の計画された更新サイクルよりも速くゾーンが変化する場合があります。そのため、これらのtzdataファイルを手動で更新する必要がある場合があります。 Oracleは、Java実装のtzdataを更新するためのツールを提供します。

正確な瞬間を処理する際の一般的なベストプラクティスは、それらをUTCで追跡することです。タイムゾーンは、自分の偏狭なタイムゾーンで値が表示されると予想されるユーザーへのプレゼンテーションなど、必要な場合にのみ適用します。 Java.timeでは、Instantクラスはタイムラインの瞬間を表します。ナノ秒の解像度のUTCで。

Instant instant = Instant.now() ;  // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ;  // Assign a time zone to view the same moment through the lens of a particular region’s wall-clock time.
Instant instant = zdt.toInstant();  // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.

ところで、JDBC 4.2以降に準拠するドライバーは、次の方法でJava.time型を直接処理できます。

  • PreparedStatement::setObject
  • ResultSet::getObject

Java.util.DateJava.sql.Timestampなどの古いレガシーデータ型は可能な限り避けてください。設計が不十分で、混乱し、欠陥があります。

これら4つはすべて、UTCのタイムライン上の瞬間の表現であることを理解してください。

  • モダン
    • Java.time.Instant
    • Java.time.OffsetDateTimeのオフセットが割り当てられたZoneOffset.UTC
  • レガシー
    • Java.util.Date
    • Java.sql.Timestamp

時刻とタイムゾーンのない日付のみの値が必要な場合は、Java.time.LocalDateを使用します。このクラスはJava.sql.Dateに取って代わります。

特定のデータベースについては、SQL標準が日時型とその処理のトピックにほとんど触れていないことに注意してください。さらに、さまざまなデータベースは大きく異なり、日時機能のサポートにおいて、私は本当に広くを意味します。実質的にサポートがないものもあります。 SQL標準型と、標準型よりも前の標準型または標準型の代替として意図されている独自の型とを混在させるものもあります。さらに、JDBCドライバーの動作は、データベースとの間で日時値をマーシャリングすることで異なります。ドキュメンテーションと実践、実践、実践を必ず学習してください。

Table of date-time types in Java (both legacy and modern) and in the SQL standard.

41
Basil Bourque

または、Java 8's Instant?を使用する必要がありますか?Instantを使用する場合、時間なしで日付部分のみを保存する可能性はありますか?

インスタントは、ほとんどの操作に適している必要があります。

_hibernate-Java8_を_pom.xml_に追加して、Java 8 time API:

_<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-Java8</artifactId>
    <version>${version.hibernate}</version>
</dependency>
_

その後、HibernateエンティティフィールドにLocalDateまたはLocalDateTimeまたはInstantを使用できます。 @Temporal(TemporalType.TIMESTAMP)を削除する必要があります。

私の要件は、すべての日付と日時をUTCタイムゾーンでデータベースに保存することです。 HibernateエンティティでJava 8のLocalDateおよびLocalDateTimeを使用しています。

LocalDateとLocalDateTimeにはタイムゾーンが関連付けられていないので、それは正しいですか?

構成コードのどこかにデフォルトのJVMタイムゾーンを設定できます。

_@PostConstruct
void setUTCTimezone() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
_

次に、コード内でUTC時間を操作します。

DTOでJava 8つの日付タイプを使用するには、 Jsr310JpaConverters を追加する必要があります。

_<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
_

そして:

_@EntityScan(basePackageClasses = { Application.class,    Jsr310JpaConverters.class })
SpringBootApplication
public class Application { … }
_

より多くのオプション:

7
Justas