Mysql
 sql >> база данни >  >> RDS >> Mysql

Как да съхранявате дата-час в UTC в база данни с помощта на EclipseLink и Joda-Time?

Date е агностичен за часовата зона в Java. Винаги отнема UTC (по подразбиране и винаги), но когато Date / Timestamp се предава през JDBC драйвер към база данни, той интерпретира дата/час според часовата зона на JVM, която по подразбиране на свой ред е часова зона на системата (родната зона на операционната система).

Следователно, освен ако MySQL JDBC драйверът не е бил изрично принуден да използва UTC зоната или самата JVM е настроена да използва тази зона, той няма да съхранява Date / Timestamp в целевата база данни, използвайки UTC, въпреки че самият MySQL трябваше да бъде конфигуриран да използва UTC с помощта на default_time_zone='+00:00' в my.ini или my.cnf в [mysqld] раздел. Някои бази данни като Oracle може да поддържат времеви печат с часова зона и това може да е изключение, с което не съм запознат (неизпробван, тъй като в момента нямам тази среда).

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

Това може да бъде допълнително изяснено чрез проверка на извикването на setTimestampInternal() метод за реализация на MySQL JDBC драйвер.

Вижте следните две извиквания към setTimestampInternal() метод от двете претоварени версии на setTimestamp() метод.

Когато няма Calendar екземплярът се посочва с PreparedStatement#setTimestamp() метод, ще се използва часовата зона по подразбиране (this.connection.getDefaultTimeZone() ).

Докато използвате пул за връзки в сървъри на приложения / контейнери за сервлети, подкрепени от връзка / JNDI осъществява достъп или работи с източници на данни като,

драйверът MySQL JDBC трябва да бъде принуден да използва желаната часова зона, която ни интересува (UTC), следните два параметъра трябва да бъдат предоставени чрез низа на заявката на URL адреса на връзката.

Не съм запознат с историята на MySQL JDBC драйверите, но в относително по-старите версии на драйверите на MySQL този параметър useLegacyDatetimeCode може да не е необходимо. По този начин може да се наложи човек да се коригира в този случай.

В случай на сървъри на приложения, GlassFish, например, те могат да бъдат зададени, докато създавате JDBC сфера заедно с JDBC пул за връзки вътре в самия сървър заедно с други конфигурируеми свойства или с помощта на инструмента за уеб GUI на администратора или в domain.xml директно. domain.xml изглежда по следния начин (използвайки XA източник на данни).

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

В случай на WildFly, те могат да бъдат конфигурирани в standalone-xx.yy.xml като използвате CLI команди или с помощта на инструмента за уеб GUI на администратора (използвайки източник на данни XA).

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

Същото нещо е приложимо за източници на данни, които не са XA. В този случай те могат да бъдат добавени директно към самия URL адрес на връзката.

Тези всички споменати свойства ще бъдат зададени на споменатия клас, наличен в JDBC драйвера, а именно com.mysql.jdbc.jdbc2.optional.MysqlXADataSource използвайки съответните им методи за настройка в този клас и в двата случая.

В случай на директно използване на основния JDBC API или пул на връзки в Tomcat, например, те могат да бъдат зададени директно към URL адреса на връзката (в 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>

Допълнително :

Ако целевият сървър на база данни работи в зона, чувствителна за DST и лятното часово време (DST) не е изключено, това ще причини проблеми. По-добре конфигурирайте сървъра на базата данни също така да използва стандартна часова зона, която не се влияе от DST като UTC или GMT. UTC обикновено се предпочита пред GMT, но и двете са сходни в това отношение. Цитиране директно от тази връзка .

Между другото, пуснах собствения конвертор на EclipseLink, 1 от JPA2. предоставя собствен стандартен преобразувател който може да бъде пренесен към различен доставчик на JPA, както и когато е необходимо, без малки или никакви модификации. Сега изглежда по следния начин, в който java.util.Date също беше заменен от 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);
    }
}

Тогава отговорността е единствено на свързания(ите) клиент(и) на приложението (сервлети / JSP / JSF / клиенти за отдалечен работен плот и т.н.) да преобразуват дата/час според часова зона на подходящ потребител, докато показват или представят дата/час на крайните потребители, които не е обхванат в този отговор за краткост и е извън темата въз основа на естеството на текущия въпрос.

Тези нулеви проверки в преобразувателя също не са необходими, тъй като това също е отговорност единствено на свързания(ите) клиент(и) на приложение, освен ако някои полета не са задължителни.

Всичко върви добре сега. Всякакви други предложения/препоръки са добре дошли. Критиката към всеки от моите невежи е добре дошла.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да обединя цели набори от резултати в MySQL?

  2. Създаване на таблица с числа в MySQL

  3. Как да коригирате кода на състоянието на сървъра:302 Намерен от SQL Inject Me Firefox Addon

  4. Заместител на функцията UUID версия 1 на MySQL?

  5. Как да изтрия автоматично всички референтни редове, ако родителският ред бъде изтрит в mysql?