Hibernatewritesa Java Calendar
オブジェクトをSQLに書き込む場合 TIMESTAMP
列、日付、コンピューターの日付、またはカレンダーオブジェクト(またはその他)で指定された日付を調整するタイムゾーンはどれですか?
HibernateがTIMESTAMP
をカレンダーオブジェクトに読み取る場合、日付はどのタイムゾーンに変換されますか?
HibernateがJava CalendarオブジェクトをSQLTIMESTAMP列に書き込むとき、日付、コンピューターの日付、またはカレンダーオブジェクト(またはその他)で指定された日付を調整するタイムゾーンはどれですか?
Hiberante 3.xは、CalendarType
で以下を使用します( HB-1006 を参照)。
_public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
final Calendar cal = (Calendar) value;
//st.setTimestamp( index, new Timestamp( cal.getTimeInMillis() ), cal ); //JDK 1.5 only
st.setTimestamp( index, new Timestamp( cal.getTime().getTime() ), cal );
}
_
したがって、Hibernateは PreparedStatement#setTimestamp(int, Timestamp, Calendar)
を使用します。これはカレンダーのタイムゾーンを使用します。
HibernateがTIMESTAMPをカレンダーオブジェクトに読み込むとき、日付をどのタイムゾーンに変換しますか?
さて、もう一度、CalendarType
クラスを見てみましょう。
_public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
Timestamp ts = rs.getTimestamp(name);
if (ts!=null) {
Calendar cal = new GregorianCalendar();
if ( Environment.jvmHasTimestampBug() ) {
cal.setTime( new Date( ts.getTime() + ts.getNanos() / 1000000 ) );
}
else {
cal.setTime(ts);
}
return cal;
}
else {
return null;
}
}
_
したがって、Hibernate デフォルトのロケールでデフォルトのタイムゾーンの現在の時刻を使用してデフォルトのGregorianCalendar
を構築します。
補足として、次の質問を読むことを強くお勧めします。
私はちょうど6時間同じような問題に費やし、それをここに文書化すると思いました。 Hibernateは確かにJVMタイムゾーンを使用しますが、CalendarTypeを次のように拡張することで変更できます。
public class UTCCalendarType extends CalendarType {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
/**
* This is the original code from the class, with two changes. First we pull
* it out of the result set with an example Calendar. Second, we set the new
* calendar up in UTC.
*/
@Override
public Object get(ResultSet rs, String name) throws SQLException {
Timestamp ts = rs.getTimestamp(name, new GregorianCalendar(UTC));
if (ts != null) {
Calendar cal = new GregorianCalendar(UTC);
cal.setTime(ts);
return cal;
} else {
return null;
}
}
@Override
public void set(PreparedStatement st, Object value, int index) throws SQLException {
final Calendar cal = (Calendar) value;
cal.setTimeZone(UTC);
st.setTimestamp(index, new Timestamp(cal.getTime().getTime()), cal);
}
}
ここの秘密のソースは:
rs.getTimestamp(name, new GregorianCalendar(UTC));
これにより、タイムゾーンが結果セットから必要なタイムゾーンに変換されます。したがって、私が行ったのは、このタイプをUTCカレンダーで使用し、現地時間の標準のHibernateタイプを使用することでした。笛のように滑らかに動作します...
デフォルトでは、使用するタイムゾーンを決定するのはJDBCドライバー次第です。通常、カスタムタイムゾーンを使用するようにJDBCドライバーを構成しない限り、JVMタイムゾーンが使用されます。
使用するタイムゾーンを制御する場合は、JVMレベルでタイムゾーンを設定できます。 JVMタイムゾーンをデータベースで使用されるものとは異なるものにする場合は、次のHibernate5.2構成プロパティを使用する必要があります。
<property name="hibernate.jdbc.time_zone" value="US/Eastern"/>
詳細については、 この記事 をご覧ください。
自分でコードを書きたくない場合は、オープンソースライブラリDbAssist
を使用できます。この修正を適用すると、データベース内の日付はJDBCによって処理され、UTCとして休止状態になるため、エンティティクラスを変更する必要もありません。
たとえば、Hibernate 4.3.11でJPAアノテーションを使用している場合は、次のMaven依存関係を追加します。
<dependency>
<groupId>com.montrosesoftware</groupId>
<artifactId>DbAssist-4.3.11</artifactId>
<version>1.0-RELEASE</version>
</dependency>
次に、修正を適用するだけです。
Hibernate + Spring Bootセットアップの場合、アプリケーションクラスの前に@EnableAutoConfiguration
アノテーションを追加します。
HBMファイルの場合、エンティティマッピングファイルを変更して、Date
タイプをカスタムタイプにマップする必要があります。
<property name="createdAt" type="com.montrosesoftware.dbassist.types.UtcDateType" column="created_at"/>
さまざまなHibernateバージョン(またはHBMファイル)に修正を適用する方法について詳しく知りたい場合は、 プロジェクトのgithub を参照してください。タイムゾーンシフトの問題について詳しくは、この 記事 をご覧ください。