web-dev-qa-db-ja.com

HibernateがJava CalendarオブジェクトをSQLTIMESTAMPに読み書きするときに、どのタイムゾーンを使用しますか?

Hibernatewritesa Java Calendar オブジェクトをSQLに書き込む場合 TIMESTAMP 列、日付、コンピューターの日付、またはカレンダーオブジェクト(またはその他)で指定された日付を調整するタイムゾーンはどれですか?

HibernateTIMESTAMPをカレンダーオブジェクトに読み取る場合、日付はどのタイムゾーンに変換されますか?

18
Derek Mahar

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を構築します


補足として、次の質問を読むことを強くお勧めします。

16
Pascal Thivent

私はちょうど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タイプを使用することでした。笛のように滑らかに動作します...

6
markthegrea

デフォルトでは、使用するタイムゾーンを決定するのはJDBCドライバー次第です。通常、カスタムタイムゾーンを使用するようにJDBCドライバーを構成しない限り、JVMタイムゾーンが使用されます。

使用するタイムゾーンを制御する場合は、JVMレベルでタイムゾーンを設定できます。 JVMタイムゾーンをデータベースで使用されるものとは異なるものにする場合は、次のHibernate5.2構成プロパティを使用する必要があります。

<property name="hibernate.jdbc.time_zone" value="US/Eastern"/>

詳細については、 この記事 をご覧ください。

1
Vlad Mihalcea

自分でコードを書きたくない場合は、オープンソースライブラリ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 を参照してください。タイムゾーンシフトの問題について詳しくは、この 記事 をご覧ください。

0
orim