環境
問題
別のサービスと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ブート構成内でタイムゾーンを指定する方法はありますか?
この問題を解決するための最も適切な回避策は、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
_が必要であるという事実におそらく興味があるでしょう。さらに詳しく この回答では 。
できません。 Eclipselinkは、単一のargバージョンのsetTimestamp
を使用して、タイムゾーン処理の責任をドライバーに委任します。postgresqljdbcドライバーでは、デフォルトのタイムゾーンを上書きできません。 postgresドライバーは、クライアントのタイムゾーンをセッションに伝搬することさえあるので、サーバー側のデフォルトも役に立たないでしょう。
たとえば、JPA 2.1 AttributeConverter
を書き込んでタイムスタンプを宛先ゾーンにシフトするなど、問題を回避しようとするハックなことがいくつかありますが、クライアントのタイムゾーンに夏時間の調整があるため、最終的には運命にありません。 、あいまいな場合や表現できない場合があります。
クライアントでデフォルトのタイムゾーンを設定するか、ネイティブSQLにドロップして、タイムスタンプをキャスト付きの文字列として設定する必要があります。