Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie speichert man Datum und Uhrzeit in UTC mit EclipseLink und Joda-Time in einer Datenbank?

Date ist in Java zeitzonenunabhängig. Es nimmt immer UTC (standardmäßig und immer), aber wenn Date / Timestamp über einen JDBC-Treiber an eine Datenbank übergeben wird, interpretiert er Datum/Uhrzeit gemäß der JVM-Zeitzone, die wiederum standardmäßig die Systemzeitzone (die native Betriebssystemzone) ist.

Daher würde Date nicht gespeichert, es sei denn, der MySQL-JDBC-Treiber wurde ausdrücklich gezwungen, die UTC-Zone zu verwenden, oder JVM selbst ist darauf eingestellt, diese Zone zu verwenden / Timestamp in die Zieldatenbank unter Verwendung von UTC, obwohl MySQL selbst für die Verwendung von UTC mit default_time_zone='+00:00' konfiguriert werden sollte in my.ini oder my.cnf im [mysqld] Sektion. Einige Datenbanken wie Oracle unterstützen möglicherweise Zeitstempel mit Zeitzone und es kann eine Ausnahme sein, mit der ich nicht vertraut bin (ungetestet, da ich diese Umgebung derzeit nicht habe).

void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException aus

Dies kann weiter verdeutlicht werden, indem der Aufruf von setTimestampInternal() Methode der Implementierung des MySQL-JDBC-Treibers.

Siehe die folgenden zwei Aufrufe von setTimestampInternal() -Methode aus den beiden überladenen Versionen von setTimestamp() Methode.

Wenn kein Calendar -Instanz wird mit PreparedStatement#setTimestamp() angegeben -Methode wird die Standardzeitzone verwendet (this.connection.getDefaultTimeZone() ).

Bei der Verwendung eines Verbindungspools in Anwendungsservern / Servlet-Containern, die von einer Verbindung / JNDI unterstützt werden, die auf Datenquellen wie

zugreifen oder mit ihnen arbeiten

der MySQL JDBC-Treiber gezwungen werden muss, die gewünschte Zeitzone unseres Interesses (UTC) zu verwenden, müssen die folgenden zwei Parameter über die Abfragezeichenfolge der Verbindungs-URL bereitgestellt werden.

Ich bin mit der Geschichte der MySQL-JDBC-Treiber nicht vertraut, aber in relativ älteren Versionen von MySQL-Treibern ist dieser Parameter useLegacyDatetimeCode möglicherweise nicht erforderlich. Daher muss man sich in diesem Fall möglicherweise anpassen.

Im Falle von Anwendungsservern, GlassFish, können sie beispielsweise beim Erstellen eines JDBC-Realms zusammen mit einem JDBC-Verbindungspool innerhalb des Servers selbst zusammen mit anderen konfigurierbaren Eigenschaften entweder mit dem Admin-Web-GUI-Tool oder in domain.xml direkt. domain.xml sieht wie folgt aus (unter Verwendung einer XA-Datenquelle).

<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
                      name="jdbc_pool"
                      res-type="javax.sql.XADataSource">

  <property name="password" value="password"></property>
  <property name="databaseName" value="database_name"></property>
  <property name="serverName" value="localhost"></property>
  <property name="user" value="root"></property>
  <property name="portNumber" value="3306"></property>
  <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
  <property name="characterEncoding" value="UTF-8"></property>
  <property name="useUnicode" value="true"></property>
  <property name="characterSetResults" value="UTF-8"></property>
  <!-- The following two of our interest -->
  <property name="serverTimezone" value="UTC"></property>
  <property name="useLegacyDatetimeCode" value="false"></property>
</jdbc-connection-pool>

<jdbc-resource pool-name="jdbc_pool" 
               description="description"
               jndi-name="jdbc/pool">
</jdbc-resource>

Im Fall von WildFly können sie in standalone-xx.yy.xml konfiguriert werden Verwenden von CLI-Befehlen oder Verwenden des Admin-Web-GUI-Tools (unter Verwendung einer XA-Datenquelle).

<xa-datasource jndi-name="java:jboss/datasources/datasource_name"
               pool-name="pool_name"
               enabled="true"
               use-ccm="true">

    <xa-datasource-property name="DatabaseName">database_name</xa-datasource-property>
    <xa-datasource-property name="ServerName">localhost</xa-datasource-property>
    <xa-datasource-property name="PortNumber">3306</xa-datasource-property>
    <xa-datasource-property name="UseUnicode">true</xa-datasource-property>
    <xa-datasource-property name="CharacterEncoding">UTF-8</xa-datasource-property>
    <!-- The following two of our interest -->
    <xa-datasource-property name="UseLegacyDatetimeCode">false</xa-datasource-property>
    <xa-datasource-property name="ServerTimezone">UTC</xa-datasource-property>

    <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
    <driver>mysql</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>

    <xa-pool>
        <min-pool-size>5</min-pool-size>
        <max-pool-size>15</max-pool-size>
    </xa-pool>

    <security>
        <user-name>root</user-name>
        <password>password</password>
    </security>

    <validation>
        <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
        <background-validation>true</background-validation>
        <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
    </validation>

    <statement>
        <share-prepared-statements>true</share-prepared-statements>
    </statement>
</xa-datasource>

<drivers>
    <driver name="mysql" module="com.mysql">
        <driver-class>com.mysql.jdbc.Driver</driver-class>
    </driver>
</drivers>

Das Gleiche gilt für Nicht-XA-Datenquellen. Sie können in diesem Fall direkt an die Verbindungs-URL selbst angehängt werden.

Diese alle erwähnten Eigenschaften werden auf die erwähnte Klasse gesetzt, die im JDBC-Treiber verfügbar ist, nämlich com.mysql.jdbc.jdbc2.optional.MysqlXADataSource Verwenden Sie in beiden Fällen ihre jeweiligen Setter-Methoden in dieser Klasse.

Im Falle der direkten Verwendung der Kern-JDBC-API oder des Verbindungspoolings in Tomcat können sie beispielsweise direkt auf die Verbindungs-URL (in context.xml) gesetzt werden )

<Context antiJARLocking="true" path="/path">
    <Resource name="jdbc/pool" 
              auth="Container"
              type="javax.sql.DataSource"
              maxActive="100"
              maxIdle="30"
              maxWait="10000"
              username="root"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/database_name?useEncoding=true&amp;characterEncoding=UTF-8&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"/>
</Context>

Zusätzlich :

Wenn der Zieldatenbankserver in einer DST-empfindlichen Zone ausgeführt wird und die Sommerzeit (DST) nicht deaktiviert ist, führt dies zu Problemen. Konfigurieren Sie den Datenbankserver besser auch so, dass er eine Standardzeitzone verwendet, die nicht von der Sommerzeit beeinflusst wird, wie UTC oder GMT. UTC wird normalerweise gegenüber GMT bevorzugt, aber beide sind in dieser Hinsicht ähnlich. Zitat direkt aus dieser Link .

Übrigens habe ich den proprietären Konverter von EclipseLink, seit JPA 2.1 fallen gelassen bietet einen eigenen Standardkonverter die bei Bedarf ohne kleine oder gar keine Änderungen auf einen anderen JPA-Anbieter portiert werden können. Es sieht nun wie folgt aus, in dem java.util.Date wurde ebenfalls durch java.sql.Timestamp ersetzt .

import java.sql.Timestamp;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

@Converter(autoApply = true)
public final class JodaDateTimeConverter implements AttributeConverter<DateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(DateTime dateTime) {
        return dateTime == null ? null : new Timestamp(dateTime.withZone(DateTimeZone.UTC).getMillis());
    }

    @Override
    public DateTime convertToEntityAttribute(Timestamp timestamp) {
        return timestamp == null ? null : new DateTime(timestamp, DateTimeZone.UTC);
    }
}

Es liegt dann allein in der Verantwortung der zugeordneten Anwendungsclients (Servlets / JSP / JSF / Remote-Desktop-Clients usw.), Datum / Uhrzeit gemäß der Zeitzone eines geeigneten Benutzers zu konvertieren, während Datum / Uhrzeit Endbenutzern angezeigt oder präsentiert werden wird in dieser Antwort der Kürze halber nicht behandelt und ist aufgrund der Art der aktuellen Frage nicht zum Thema.

Diese Nullprüfungen im Konverter sind ebenfalls nicht erforderlich, da dies ebenfalls ausschließlich in der Verantwortung des/der zugeordneten Anwendungsclient(s) liegt, es sei denn, einige Felder sind optional.

Alles geht jetzt gut. Alle anderen Vorschläge/Empfehlungen sind willkommen. Kritik an alle Unwissenden von mir ist sehr willkommen.