sql >> Database teknologi >  >> RDS >> Mysql

Hvordan gemmer man dato-tid i UTC i en database ved hjælp af EclipseLink og Joda-Time?

Dato er tidszoneagnostisk i Java. Det tager altid UTC (som standard og altid), men når Dato / Tidsstempel sendes gennem en JDBC-driver til en database, fortolker den dato/klokkeslæt i henhold til JVM-tidszonen, som igen er standard til systemets tidszone (den oprindelige operativsystemzone).

Medmindre MySQL JDBC-driveren eksplicit blev tvunget til at bruge UTC-zonen, eller JVM selv er indstillet til at bruge denne zone, vil den derfor ikke gemme Dato / Tidsstempel ind i måldatabasen ved hjælp af UTC, selvom MySQL selv skulle konfigureres til at bruge UTC ved hjælp af default_time_zone='+00:00' i my.ini eller my.cnf i [mysqld] afsnit. Nogle databaser som Oracle understøtter muligvis tidsstempel med tidszone, og det kan være en undtagelse, som jeg ikke er bekendt med (utestet, da jeg ikke har det miljø på nuværende tidspunkt).

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

Dette kan yderligere afklares ved at kontrollere påkaldelsen af ​​ setTimestampInternal() metode til implementering af MySQL JDBC-driveren.

Se følgende to kalder til setTimestampInternal() metode fra de to overbelastede versioner af setTimestamp() metode.

Når ingen Kalender instans er angivet med PreparedStatement#setTimestamp() metode, vil standardtidszonen blive brugt (this.connection.getDefaultTimeZone() ).

Mens du bruger en forbindelsespulje i applikationsservere / Servlet-containere understøttet af en forbindelse / JNDI, der får adgang til eller opererer på datakilder som,

MySQL JDBC-driveren skal tvinges til at bruge den ønskede tidszone af vores interesse (UTC), de følgende to parametre skal leveres gennem forespørgselsstrengen i forbindelses-URL'en.

Jeg er ikke bekendt med historien om MySQL JDBC-drivere, men i relativt ældre versioner af MySQL-drivere er denne parameter useLegacyDatetimeCode er muligvis ikke nødvendig. Således kan man kræve at justere sig selv i så fald.

I tilfælde af applikationsservere, GlassFish, for eksempel, kan de indstilles, mens de opretter et JDBC-rige sammen med en JDBC-forbindelsespulje inde i selve serveren sammen med andre konfigurerbare egenskaber enten ved hjælp af admin web-GUI-værktøjet eller i domain.xml direkte. domæne.xml ser ud som følgende (ved hjælp af en XA-datakilde).

<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>

I tilfælde af WildFly kan de konfigureres i standalone-xx.yy.xml ved at bruge CLI-kommandoer eller ved at bruge admin web-GUI-værktøjet (ved at bruge en XA-datakilde).

<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>

Det samme gælder for ikke-XA-datakilder. De kan i så fald føjes direkte til selve forbindelses-URL'en.

Disse alle nævnte egenskaber vil blive sat til den nævnte klasse, der er tilgængelig i JDBC-driveren, nemlig com.mysql.jdbc.jdbc2.optional.MysqlXADataSource ved at bruge deres respektive setter-metoder i denne klasse i begge tilfælde.

I tilfælde af at bruge kerne-JDBC API direkte eller forbindelsespooling i Tomcat, for eksempel, kan de indstilles direkte til forbindelses-URL'en (i context.xml )

<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>

Yderligere:

Hvis måldatabaseserveren kører på en DST-følsom zone, og sommertid (DST) ikke er slået fra, vil det forårsage problemer. Konfigurer hellere databaseserveren til også at bruge en standard tidszone, som ikke er påvirket af sommertid som UTC eller GMT. UTC foretrækkes normalt frem for GMT, men begge er ens i denne henseende. Citerer direkte fra dette link .

I øvrigt droppede jeg den proprietære konverter af EclipseLink, siden JPA 2.1 leverer sin egen standardkonverter som kan overføres til en anden JPA-udbyder efter behov uden små eller ingen ændringer overhovedet. Det ser nu ud som følgende, hvor java.util.Date blev også erstattet af java.sql.Timestamp .

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

Det er derefter udelukkende ansvaret for den eller de tilknyttede applikationsklienter (Servlets / JSP / JSF / remote desktop-klienter osv.) at konvertere dato / klokkeslæt i henhold til en passende brugers tidszone, mens du viser eller præsenterer dato / tid for slutbrugere, som er ikke dækket i dette svar for kortheds skyld og er off-topic baseret på arten af ​​det aktuelle spørgsmål.

Disse nul-tjek i konverteren er heller ikke nødvendige, da det også udelukkende er de tilknyttede applikationsklienters ansvar, medmindre nogle felter er valgfrie.

Alt går fint nu. Andre forslag/anbefalinger er velkomne. Kritik til enhver af mine uvidende er meget velkommen.



  1. Indsæt flere elementer i et ID MySQL fra afkrydsningsfeltet input PHP-formular

  2. MySQL fejlkode:1193. Ukendt systemvariabel

  3. Liste over alle indekser i en SQLite-database

  4. Tilføj en genereret kolonne til en tabel i SQLite