webentwicklung-frage-antwort-db.com.de

Festlegen der UTC-Zeitzone für den Spring Boot JPA-Zeitstempel

Umgebung

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

Problem

Ich baue einen Spring Boot-Microservice, der eine Postgresql-Datenbank mit einem anderen Dienst teilt. Die Datenbank wird extern initialisiert (außerhalb unserer Kontrolle) und der vom anderen Service verwendete Datetime-Spaltentyp ist timestamp ohne Zeitzone . Da ich möchte, dass alle Daten in der Datenbank denselben Typ haben, ist es für meine JPA-Entitätstermine erforderlich, dass dieser Typ vorhanden ist.

Wie ich sie auf meine JPA-Entity-Objekte abbilde, sieht folgendermaßen aus:

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

Das Problem ist, dass ich beim Erstellen eines Zeitstempels Folgendes beachte: 

new Java.sql.Timestamp(System.currentTimeMillis())

und ich schaue in die Datenbank, der Zeitstempel enthält das Datum und die Uhrzeit meiner lokalen Zeitzone, aber ich möchte ihn in UTC speichern. Dies liegt daran, dass meine Standard-Zeitzone auf 'Europe/Brussels' gesetzt ist und JPA/JDBC mein Java.sql.Timestamp-Objekt in meine Zeitzone konvertiert, bevor es in die Datenbank eingefügt wird.

Nicht ideale Lösungen gefunden

  • TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC")); hat den Effekt, den ich erreichen möchte, aber es ist nicht geeignet, weil es nicht spezifisch für meinen Dienst ist. Das heißt Dies wirkt sich auf die gesamte JVM oder den aktuellen Thread und untergeordnete Elemente aus.

  • Das Starten der Anwendung mit -Duser.timezone=GMT scheint den Job auch für eine einzelne Instanz einer laufenden JVM auszuführen. Daher eine bessere Lösung als die vorige.

Gibt es eine Möglichkeit, die Zeitzone in der Konfiguration von JPA/Datenquelle/Federstiefel anzugeben?

7
oizulain

Die geeignetste Lösung für das Problem ist die Verwendung eines AttributeConverter, um Java 8 ZonedDateTime -Objekte in Java.sql.Timestamp - Objekte zu konvertieren. damit sie dem PostgreSQL-Typ timestamp without time zone zugeordnet werden können.

Der Grund, warum Sie ein AttributeConverter benötigen, ist, dass die Zeittypen Java 8/Joda time date time sind noch nicht mit JPA kompatibel .

Das AttributeConverter sieht so aus:

@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")));
    }

}

Auf diese Weise kann ich die Datenbank-Zeitstempel lesen, die nicht Zeitzoneninformationen als ZonedDateTime -Objekte haben, die [~ # ~] utc [~ # ~] haben Zeitzone. Auf diese Weise behalte ich die genaue Datumszeit, die auf der Datenbank angezeigt wird unabhängig von der Zeitzone, in der meine App ausgeführt wird.

Da toLocalDateTime() auch die Standard-Zeitzonen-Konvertierung des Systems anwendet, bricht dieses AttributeConverter die vom JDBC-Treiber angewendete Konvertierung grundsätzlich ab.

Müssen Sie wirklich timestamp without timezone Verwenden?

In Wirklichkeit ist der PostgreSQL-Typ timestamp without timezone Die falsche Wahl, wenn Sie Datums- und Uhrzeitinformationen in einer Zeitzone speichern (auch wenn es sich um UTC handelt). Der richtige Datentyp wäre timestamp with timezone, Der die Zeitzoneninformationen enthält. Mehr zu diesem Thema hier .

Wenn Sie jedoch aus irgendeinem Grund müssen den timestamp without timezone - Ansatz verwenden, ist der oben beschriebene ZonedDateTime -Ansatz meiner Meinung nach eine robuste und konsistente Lösung.

Serialisieren Sie auch das ZonedDateTime in JSON?

Dann interessiert Sie wahrscheinlich die Tatsache, dass Sie mindestens die Version 2.6.0 Der jackson-datatype-jsr310 - Abhängigkeit benötigen, damit die Serialisierung funktioniert. Mehr dazu in dieser Antwort .

6
oizulain

Du kannst nicht Eclipselink verwendet die Version mit einem einzelnen Argument von setTimestamp, wobei die Zuständigkeit für die Zeitzonenbehandlung an den Treiber delegiert wird. Der jdbc-Treiber für postgresql lässt die Standardzeitzone nicht überschreiben. Der Postgres-Treiber gibt sogar die Client-Zeitzone an die Sitzung weiter, sodass die serverseitigen Standardeinstellungen für Sie ebenfalls nicht von Nutzen sind.

Es gibt einige harte Dinge, mit denen Sie versuchen könnten, das Problem zu umgehen, z. B. das Schreiben einer JPA 2.1 AttributeConverter, um Ihre Zeitmarken in die Zielzone zu verschieben. Letztendlich sind sie jedoch zum Scheitern verurteilt, da in Ihrer Client-Zeitzone Sommerzeit-Anpassungen vorgenommen werden, die manchmal mehrdeutig sind oder nicht darstellbar.

Sie müssen die Standardzeitzone auf Ihrem Client festlegen oder in natives SQL ablegen, um Zeitstempel als Zeichenfolgen mit Besetzungen festzulegen.

1
teppic