Hibernate在读取和写入Java Calendar对象到SQL TIMESTAMP时使用的时区是什么?

时间:2010-11-07 10:00:24

标签: java sql hibernate timestamp

Hibernate 将<{3}}对象写入SQL Calendar列时,它会调整日期,计算机的日期或在日历对象(或其他一些)中指定的?

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

4 个答案:

答案 0 :(得分:15)

  

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

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


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

答案 1 :(得分:6)

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

答案 2 :(得分:1)

默认情况下,由JDBC Driver决定使用哪个时区。通常,除非您将JDBC驱动程序配置为使用自定义时区,否则将使用JVM时区。

如果要控制使用的时区,可以在JVM级别设置时区。如果您希望JVM时区与数据库使用的时区不同,则需要使用以下Hibernate 5.2配置属性:

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

有关详细信息,请查看this article

答案 3 :(得分:0)

如果您不想自己编写代码,可以使用开源库DbAssist。应用此修复程序后,数据库中的日期将由JDBC处理,然后Hibernate将作为UTC处理,因此您甚至不必更改权限类。

例如,如果您在Hibernate 4.3.11中使用JPA Annotations,请添加以下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文件)应用修复程序的更多信息,请参阅the project's github。您还可以在此article中详细了解时区转换问题。