sql >> Database >  >> RDS >> Mysql

Hoe de datum-tijd in UTC op te slaan in een database met behulp van EclipseLink en Joda-Time?

Date is tijdzone-agnostisch in Java. Het duurt altijd UTC (standaard en altijd), maar wanneer Date / Timestamp door een JDBC-stuurprogramma wordt doorgegeven aan een database, interpreteert het de datum / tijd volgens de JVM-tijdzone die op zijn beurt standaard naar de systeemtijdzone gaat (de native besturingssysteemzone).

Daarom, tenzij het MySQL JDBC-stuurprogramma expliciet werd gedwongen om de UTC-zone te gebruiken of JVM zelf is ingesteld om die zone te gebruiken, zou het Date niet opslaan / Timestamp in de doeldatabase met behulp van UTC, hoewel MySQL zelf zou worden geconfigureerd om UTC te gebruiken met behulp van default_time_zone='+00:00' in my.ini of my.cnf in de [mysqld] sectie. Sommige databases zoals Oracle ondersteunen mogelijk een tijdstempel met tijdzone en het kan een uitzondering zijn die ik niet ken (niet getest omdat ik die omgeving momenteel niet heb).

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

Dit kan verder worden verduidelijkt door de aanroep van setTimestampInternal() methode van de implementatie van het MySQL JDBC-stuurprogramma.

Zie de volgende twee aanroepen naar de setTimestampInternal() methode vanuit de twee overbelaste versies van de setTimestamp() methode.

Wanneer geen Calendar instantie is opgegeven met de PreparedStatement#setTimestamp() methode wordt de standaardtijdzone gebruikt (this.connection.getDefaultTimeZone() ).

Tijdens het gebruik van een verbindingspool in applicatieservers / Servlet-containers ondersteund door een verbinding / JNDI die toegang heeft tot of werkt op gegevensbronnen zoals,

het MySQL JDBC-stuurprogramma moet worden gedwongen om de gewenste tijdzone van ons belang (UTC) te gebruiken, de volgende twee parameters moeten worden opgegeven via de queryreeks van de verbindings-URL.

Ik ben niet bekend met de geschiedenis van MySQL JDBC-stuurprogramma's, maar in relatief oudere versies van MySQL-stuurprogramma's, deze parameter useLegacyDatetimeCode misschien niet nodig. In dat geval kan het dus nodig zijn om jezelf aan te passen.

In het geval van toepassingsservers, bijvoorbeeld GlassFish, kunnen ze worden ingesteld terwijl een JDBC-realm wordt gemaakt, samen met een JDBC-verbindingspool binnen de server zelf, samen met andere configureerbare eigenschappen, ofwel met behulp van de admin-web-GUI-tool of in domain.xml direct. domain.xml ziet er als volgt uit (met een XA-gegevensbron).

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

In het geval van WildFly kunnen ze worden geconfigureerd in standalone-xx.yy.xml met behulp van CLI-commando's of met behulp van de admin web GUI-tool (met behulp van een XA-gegevensbron).

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

Hetzelfde is van toepassing op niet-XA-gegevensbronnen. Ze kunnen in dat geval direct worden toegevoegd aan de verbindings-URL zelf.

Deze alle genoemde eigenschappen worden ingesteld op de genoemde klasse die beschikbaar is in het JDBC-stuurprogramma, namelijk com.mysql.jdbc.jdbc2.optional.MysqlXADataSource in beide gevallen hun respectievelijke setter-methoden in deze klasse gebruiken.

In het geval van rechtstreeks gebruik van de JDBC-kern-API, of pooling van verbindingen in Tomcat, bijvoorbeeld, kunnen ze rechtstreeks worden ingesteld op de verbindings-URL (in 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>

Aanvullend:

Als de doeldatabaseserver in een DST-gevoelige zone draait en de zomertijd (DST) niet is uitgeschakeld, zal dit problemen veroorzaken. Configureer de databaseserver ook beter om een ​​standaardtijdzone te gebruiken die niet wordt beïnvloed door DST, zoals UTC of GMT. UTC heeft meestal de voorkeur boven GMT, maar beide zijn in dit opzicht vergelijkbaar. Rechtstreeks citeren van deze link .

Trouwens, ik heb de propriëtaire converter van EclipseLink laten vallen, sinds JPA 2.1 biedt zijn eigen standaardconverter die indien nodig kan worden geport naar een andere JPA-provider zonder enige of geen enkele wijziging. Het ziet er nu als volgt uit waarin java.util.Date werd ook vervangen door 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);
    }
}

Het is dan uitsluitend de verantwoordelijkheid van de bijbehorende applicatieclient(s) (Servlets / JSP / JSF / remote desktop-clients enz.) om de datum / tijd te converteren volgens de tijdzone van een geschikte gebruiker, terwijl de datum / tijd wordt weergegeven of gepresenteerd aan eindgebruikers die wordt kortheidshalve niet behandeld in dit antwoord en is off-topic op basis van de aard van de huidige vraag.

Die null-controles in de converter zijn ook niet nodig, aangezien het ook uitsluitend de verantwoordelijkheid is van de bijbehorende applicatieclient(s), tenzij sommige velden optioneel zijn.

Alles gaat nu goed. Andere suggesties/aanbevelingen zijn welkom. Kritiek op onwetenden van mij is van harte welkom.



  1. ASP.NET Lidmaatschap/rolproviders voor MySQL?

  2. TO_DATE() Functie in Oracle

  3. Hoe MySQL-server te starten, opnieuw op te starten, de status te controleren en te stoppen?

  4. Hoe datetime te vergelijken met alleen datum in SQL Server