web-dev-qa-db-ja.com

Spring Boot JPAタイムスタンプのUTCタイムゾーンを指定する方法

環境

  • Spring Boot Starter Data JPA 1.4.2
  • Eclipselink 2.5.0
  • Postgresql 9.4.1211.jre7

問題

別のサービスとPostgresqlデータベースを共有するSpring Bootマイクロサービスを構築しています。データベースは外部で(私たちの制御外で)初期化され、他のサービスで使用される日時列のタイプはタイムゾーンのないタイムスタンプです。したがって、データベースのすべての日付に同じタイプを設定したいので、JPAエンティティの日付にはそのタイプが必須です。

JPAエンティティオブジェクトにマッピングする方法は次のとおりです。

_@Column(name = "some_date", nullable = false)
private Timestamp someDate;
_

問題は、次のようにタイムスタンプを作成することです。

_new Java.sql.Timestamp(System.currentTimeMillis())
_

データベースを見ると、タイムスタンプにローカルタイムゾーンの日時が含まれていますが、UTCに保存したいと思います。これは、デフォルトのタイムゾーンが「ヨーロッパ/ブリュッセル」に設定されており、JPA/JDBCがデータベースに書き込む前に_Java.sql.Timestamp_オブジェクトをタイムゾーンに変換するためです。

理想的ではないソリューションが見つかりました

  • TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));は私が達成したい効果がありますが、それは私のサービスに固有ではないため、適切ではありません。つまりこれは、JVM全体、または現在のスレッドと子に影響します。

  • _-Duser.timezone=GMT_を使用してアプリケーションを起動すると、実行中のJVMの単一のインスタンスに対しても機能するようです。したがって、以前のソリューションよりも優れたソリューションです。

しかし、JPA/datasource/springブート構成内でタイムゾーンを指定する方法はありますか?

8
oizulain

この問題を解決するための最も適切な回避策は、AttributeConverterを使用してJava 8 ZonedDateTimeオブジェクトを_Java.sql.Timestamp_オブジェクトに変換し、PostgreSQL _timestamp without time zone_タイプ。

AttributeConverterが必要な理由は、Java 8/Joda time date time types まだJPAとの互換性がないため であるためです。

AttributeConverterは次のようになります。

_@Converter(autoApply = true)
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(ZonedDateTime zonedDateTime) {
        return (zonedDateTime == null ? null : Timestamp.valueOf(zonedDateTime.toLocalDateTime()));
    }

    @Override
    public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
        return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime().atZone(ZoneId.of("UTC")));
    }

}
_

[〜#〜] utc [〜#〜]タイムゾーンを持つZonedDateTimeオブジェクトとしてタイムゾーン情報があるしないのデータベースタイムスタンプを読み取ることができます。このようにして、データベースに表示される正確な日時を保持しますアプリが実行されるタイムゾーンに関係なく

toLocalDateTime() はシステムのデフォルトのタイムゾーン変換も適用するため、このAttributeConverterは基本的に、JDBCドライバーによって適用される変換をキャンセルします。

本当に_timestamp without timezone_を使用する必要がありますか?

実際には、タイムゾーンに日付と時刻の情報を格納している場合(UTCであっても)、_timestamp without timezone_ PostgreSQLの型は間違った選択です。使用する正しいデータ型は、タイムゾーン情報を含む_timestamp with timezone_です。このトピックの詳細 ここ

ただし、何らかの理由でmust _timestamp without timezone_を使用する場合、上記のZonedDateTimeアプローチは堅牢で一貫性のあるソリューションだと思います。

ZonedDateTimeをJSONにシリアル化していますか?

次に、シリアライゼーションが機能するためには、少なくとも_2.6.0_依存関係のバージョン_jackson-datatype-jsr310_が必要であるという事実におそらく興味があるでしょう。さらに詳しく この回答では

6
oizulain

できません。 Eclipselinkは、単一のargバージョンのsetTimestampを使用して、タイムゾーン処理の責任をドライバーに委任します。postgresqljdbcドライバーでは、デフォルトのタイムゾーンを上書きできません。 postgresドライバーは、クライアントのタイムゾーンをセッションに伝搬することさえあるので、サーバー側のデフォルトも役に立たないでしょう。

たとえば、JPA 2.1 AttributeConverterを書き込んでタイムスタンプを宛先ゾーンにシフトするなど、問題を回避しようとするハックなことがいくつかありますが、クライアントのタイムゾーンに夏時間の調整があるため、最終的には運命にありません。 、あいまいな場合や表現できない場合があります。

クライアントでデフォルトのタイムゾーンを設定するか、ネイティブSQLにドロップして、タイムスタンプをキャスト付きの文字列として設定する必要があります。

1
teppic