Date
Javaではタイムゾーンに依存しません。常にUTC(デフォルトおよび常に)がかかりますが、Date
/Timestamp
JDBCドライバーを介してデータベースに渡され、JVMタイムゾーンに従って日付/時刻を解釈します。JVMタイムゾーンは、デフォルトでシステムタイムゾーン(ネイティブオペレーティングシステムゾーン)になります。
したがって、MySQL JDBCドライバーがUTCゾーンの使用を明示的に強制されていない限り、またはJVM自体がそのゾーンを使用するように設定されていない限り、Date
は保存されません。 /Timestamp
MySQL自体がdefault_time_zone='+00:00'
を使用してUTCを使用するように構成されている場合でも、UTCを使用してターゲットデータベースに追加します。 my.ini
で またはmy.cnf
[mysqld]
で セクション。 Oracleのような一部のデータベースは、タイムゾーン付きのタイムスタンプをサポートしている場合がありますが、これは私がよく知らない例外である可能性があります(現在、その環境がないため、テストされていません)。
void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
これは、setTimestampInternal()
MySQLJDBCドライバー実装のメソッド。
次のsetTimestampInternal()
の呼び出し setTimestamp()
の2つのオーバーロードされたバージョン内からのメソッド メソッド。
Calendar
がない場合 インスタンスはPreparedStatement#setTimestamp()
で指定されます メソッドでは、デフォルトのタイムゾーンが使用されます(this.connection.getDefaultTimeZone()
。
アプリケーションサーバーで接続プールを使用しているとき/接続に裏打ちされたサーブレットコンテナ/JNDIが次のようなデータソースにアクセスまたは操作しているとき
-
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
(xa) -
com.mysql.jdbc.jdbc2.optional.MysqlDataSource
(非xa)
MySQL JDBCドライバーは、目的のタイムゾーン(UTC)を使用するように強制する必要があります。次の2つのパラメーターは、接続URLのクエリ文字列を介して提供する必要があります。
MySQL JDBCドライバーの履歴に精通していませんが、比較的古いバージョンのMySQLドライバーでは、このパラメーターuseLegacyDatetimeCode
必要ないかもしれません。したがって、その場合は自分で調整する必要があるかもしれません。
たとえば、アプリケーションサーバーの場合、GlassFishは、管理WebGUIツールまたは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コマンドを使用するか、管理Web 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&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=UTC"/>
</Context>
追加:
ターゲットデータベースサーバーがDST対応ゾーンで実行されていて、夏時間(DST)がオフになっていない場合、問題が発生します。 UTCやGMTなどのDSTの影響を受けない標準のタイムゾーンを使用するようにデータベースサーバーを構成することをお勧めします。通常、UTCはGMTよりも優先されますが、どちらもこの点で類似しています。 このリンク 。
ちなみに、JPA 2.1以降、EclipseLinkのプロプライエタリコンバータであるを削除しました。独自の標準コンバーターを提供します
これは、必要に応じて、ほとんどまたはまったく変更することなく、別の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 /リモートデスクトップクライアントなど)の責任です。簡潔にするためにこの回答ではカバーされておらず、現在の質問の性質に基づいてトピックから外れています。
一部のフィールドがオプションでない限り、コンバーターでのこれらのnullチェックも、関連するアプリケーションクライアントの責任であるため、必要ありません。
今はすべてうまくいきます。その他の提案/推奨事項は大歓迎です。私の知らない人への批判は大歓迎です。