Hibernate 在读取 Java 日历对象并将其写入 SQL 时间戳时使用哪个时区?

2022-09-02 13:03:49

Hibernate 将 Java 日历对象写入 SQL TIMESTAMP 列时,它会将日期、计算机的日期或日历对象(或其他某个)中指定的日期调整到哪个时区?

当 Hibernate 将 读入日历对象时,它会将日期转换为哪个时区?TIMESTAMP


答案 1

当 Hibernate 将 Java 日历对象写入 SQL TIMESTAMP 列时,它会将日期、计算机的日期或日历对象(或其他某个)中指定的日期调整到哪个时区?

Hiberante 3.x 在 中使用以下(参见 HB-1006):CalendarType

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使用ReadyStatement#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 使用默认时区中的当前时间与默认区域设置来构造默认的公历日历。


作为旁注,我强烈建议阅读以下问题:


答案 2

我刚刚花了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类型。像哨子一样光滑...