119

日付/時刻を UTC (GMT) タイムゾーンとしてデータベースに保存するように JPA/Hibernate を設定するにはどうすればよいですか? この注釈付きの JPA エンティティを検討してください。

public class Event {
    @Id
    public int id;

    @Temporal(TemporalType.TIMESTAMP)
    public java.util.Date date;
}

日付が 2008 年 2 月 3 日午前 9 時 30 分 (太平洋標準時) の場合、UTC 時刻の 2008 年 2 月 3 日午後 5 時 30 分をデータベースに格納する必要があります。同様に、データベースから日付を取得するときに、UTC として解釈する必要があります。したがって、この場合、530pm は 530pm UTC です。表示されると、午前 9 時 30 分 (太平洋標準時) としてフォーマットされます。

4

11 に答える 11

93

properties.xmlHibernate 5.2 以降、 JPA 設定ファイルに次の設定プロパティを追加することで、UTC タイム ゾーンを強制できるようになりました。

<property name="hibernate.jdbc.time_zone" value="UTC"/>

Spring Boot を使用している場合は、次のプロパティをapplication.propertiesファイルに追加します。

spring.jpa.properties.hibernate.jdbc.time_zone=UTC
于 2016-11-05T13:24:33.783 に答える
56

私の知る限りでは、Javaアプリ全体をUTCタイムゾーンに配置する必要があり(Hibernateが日付をUTCで保存するように)、表示するときに必要なタイムゾーンに変換する必要があります(少なくとも私たちはそれを行います)こちらです)。

起動時に、次のことを行います。

TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));

そして、希望のタイムゾーンをDateFormatに設定します。

fmt.setTimeZone(TimeZone.getTimeZone("Europe/Budapest"))
于 2009-02-03T18:44:32.230 に答える
47

Hibernate は Dates のタイム ゾーンを認識しませんが (何もないため)、実際に問題を引き起こしているのは JDBC レイヤーです。どちらも、データベースとの間で読み書きするときに、デフォルトで現在のJVMタイムゾーンとの間で日付を変換するとドキュメントで述べていますResultSet.getTimestampPreparedStatement.setTimestamp

org.hibernate.type.TimestampTypeHibernate 3.5では、これらの JDBC メソッドがローカル タイム ゾーンの代わりに UTC を使用するように強制するサブクラス化によって、これに対する解決策を思いつきました。

public class UtcTimestampType extends TimestampType {

    private static final long serialVersionUID = 8088663383676984635L;

    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

    @Override
    public Object get(ResultSet rs, String name) throws SQLException {
        return rs.getTimestamp(name, Calendar.getInstance(UTC));
    }

    @Override
    public void set(PreparedStatement st, Object value, int index) throws SQLException {
        Timestamp ts;
        if(value instanceof Timestamp) {
            ts = (Timestamp) value;
        } else {
            ts = new Timestamp(((java.util.Date) value).getTime());
        }
        st.setTimestamp(index, ts, Calendar.getInstance(UTC));
    }
}

TimeType と DateType を使用する場合は、同じことを修正して修正する必要があります。欠点は、誰かがより一般的なオーバーライド方法を知らない限り、POJO のすべての Date フィールドでデフォルトの代わりにこれらの型を使用することを手動で指定する必要があることです (また、純粋な JPA 互換性を壊します)。

更新: Hibernate 3.6 はタイプ API を変更しました。3.6 では、これを実装するクラス UtcTimestampTypeDescriptor を作成しました。

public class UtcTimestampTypeDescriptor extends TimestampTypeDescriptor {
    public static final UtcTimestampTypeDescriptor INSTANCE = new UtcTimestampTypeDescriptor();

    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<X>( javaTypeDescriptor, this ) {
            @Override
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                st.setTimestamp( index, javaTypeDescriptor.unwrap( value, Timestamp.class, options ), Calendar.getInstance(UTC) );
            }
        };
    }

    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>( javaTypeDescriptor, this ) {
            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap( rs.getTimestamp( name, Calendar.getInstance(UTC) ), options );
            }
        };
    }
}

アプリの起動時に TimestampTypeDescriptor.INSTANCE を UtcTimestampTypeDescriptor のインスタンスに設定すると、すべてのタイムスタンプが保存され、POJO の注釈を変更しなくても UTC として扱われます。【まだ試していません】

于 2010-08-07T15:29:44.210 に答える
10

この一般的な問題はHibernateによって処理されると思います。しかし、そうではありません!それを正しくするためのいくつかの「ハック」があります。

私が使用するのは、日付をLongとしてデータベースに保存することです。したがって、私は常に1/1/70以降のミリ秒で作業しています。次に、日付のみを返す/受け入れるゲッターとセッターがクラスにあります。したがって、APIは同じままです。欠点は、データベースに長いものがあることです。したがって、SQLを使用すると、<、>、=の比較しかできず、派手な日付演算子は実行できません。

別のアプローチは、ここで説明されているカスタムマッピングタイプを使用することです: http ://www.hibernate.org/100.html

これに対処する正しい方法は、日付の代わりにカレンダーを使用することだと思います。カレンダーを使用すると、永続化する前にタイムゾーンを設定できます。

注:愚かなstackoverflowは私にコメントさせないので、ここにdavidaへの応答があります。

シカゴでこのオブジェクトを作成する場合:

new Date(0);

Hibernateはそれを「12/31/196918:00:00」として保持します。日付にはタイムゾーンがないはずなので、なぜ調整が行われるのかわかりません。

于 2009-02-17T17:33:51.030 に答える
8

ここでは、いくつかのタイムゾーンが運用されています。

  1. UTC の暗黙のタイムゾーンを持つ Java の Date クラス (util および sql)
  2. JVM が実行されているタイムゾーン
  3. データベース サーバーのデフォルトのタイムゾーン。

これらはすべて異なる場合があります。Hibernate/JPA には、タイムゾーン情報がデータベース サーバーに保存されていることをユーザーが簡単に確認できないという重大な設計上の欠陥があります (これにより、JVM で正しい時刻と日付を再構築できます)。

JPA / Hibernateを使用してタイムゾーンを(簡単に)保存する機能がなければ、情報が失われ、情報が失われると(可能な場合)それを構築するのに費用がかかります。

タイムゾーン情報を常に保存する方が良いと私は主張します(デフォルトである必要があります)。ユーザーは、タイムゾーンを最適化するオプションを使用する必要があります(実際には表示に影響するだけですが、任意の日付に暗黙のタイムゾーンがまだあります)。

申し訳ありませんが、この投稿は回避策を提供していません (他の場所で回答されています) が、タイムゾーン情報を常に保存することが重要である理由を合理化したものです。残念なことに、多くのコンピューター科学者やプログラミング専門家は、タイムゾーンの必要性に反対しているようです。単に「情報の損失」の観点と、それが国際化などを非常に困難にしている理由を理解していないためです。世界中を移動する組織内のクライアントと人々。

于 2010-12-13T21:07:45.103 に答える
3

標準のSQL日付と時刻タイプ、およびJSR310とJodaTimeのユーザータイプがあるSourceforgeの私のプロジェクトを見てください。すべてのタイプがオフセットの問題に対処しようとします。http://sourceforge.net/projects/usertype/を参照してください

編集:このコメントに添付されたDerek Maharの質問に応えて:

「クリス、あなたのユーザータイプはHibernate 3以降で動作しますか?– DerekMahar2010年11月7日12:30」

はい、これらのタイプはHibernate3.6を含むHibernate3.xバージョンをサポートします。

于 2010-05-01T15:35:45.360 に答える
2

日付はどのタイム ゾーンにもありません (誰にとっても同じ定義された時点からのミリ秒オフィスです) が、基になる (R)DB は通常、タイムスタンプを政治的な形式 (年、月、日、時、分、秒、. ..) タイムゾーンに敏感です。

深刻なことに、Hibernateは、DB 日付がそのようなタイムゾーンにあることを何らかの形式のマッピング内で伝えられるようにする必要があります

于 2009-03-23T19:02:36.850 に答える
1

Hibernateでは、注釈やその他の手段でタイムゾーンを指定することはできません。日付の代わりにカレンダーを使用する場合は、HIbernateプロパティAccessTypeを使用して回避策を実装し、マッピングを自分で実装できます。より高度なソリューションは、カスタムUserTypeを実装して、日付またはカレンダーをマップすることです。両方の解決策は、私のブログ投稿で説明されています: http: //www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html

于 2010-12-08T19:26:53.780 に答える
1

varchar日付を UTC として DB に保存し、明示的な変換を使用したり、Java アプリ全体を UTC タイム ゾーンに設定したりしたくないときに、まったく同じ問題が発生しましたString <-> java.util.Date(JVM が多くのアプリケーションで共有されます)。

DbAssistそのため、データベースから UTC 日付として読み取り/書き込みを簡単に修正できるオープン ソース プロジェクトがあります。JPA アノテーションを使用してエンティティ内のフィールドをマップしているため、次の依存関係を Mavenpomファイルに含めるだけで済みます。

<dependency>
    <groupId>com.montrosesoftware</groupId>
    <artifactId>DbAssist-5.2.2</artifactId>
    <version>1.0-RELEASE</version>
</dependency>

@EnableAutoConfiguration次に、Spring アプリケーション クラスの前に注釈を追加して、(Hibernate + Spring Boot の例の) 修正を適用します。他のセットアップ インストール手順とその他の使用例については、プロジェクトのgithubを参照してください。

良いことは、エンティティをまったく変更する必要がないことです。java.util.Date彼らの畑をそのままにしておくことができます。

5.2.2使用している Hibernate のバージョンに対応している必要があります。プロジェクトで使用しているバージョンはわかりませんが、提供された修正の完全なリストは、プロジェクトのgithubの wiki ページで入手できます。さまざまな Hibernate バージョンで修正が異なる理由は、Hibernate の作成者がリリース間で API を数回変更したためです。

内部的には、この修正はカスタムUtcDateType. 次に、標準を、必要なすべてのタイムゾーン処理を処理java.util.Dateするカスタムにマップします。UtcDateType型のマッピングは@Typedef、提供されたファイルの注釈を使用して実現されpackage-info.javaます。

@TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class),
package com.montrosesoftware.dbassist.types;

このようなタイム シフトが発生する理由と、それを解決するためのアプローチについて説明している記事をここで見つけることができます。

于 2016-11-08T13:31:32.043 に答える