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

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

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

当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)来使用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 Driver决定使用哪个时区。 通常,除非将JDBC驱动程序配置为使用自定义时区,否则将使用JVM时区。

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

  

有关更多详细信息,请查看此文章 。

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

例如,如果您在Hibernate 4.3.11中使用JPA Annotations,请添加以下Maven依赖项:

  com.montrosesoftware DbAssist-4.3.11 1.0-RELEASE  

然后你只需应用修复:

对于Hibernate + Spring Boot设置,在应用程序类之前添加@EnableAutoConfiguration注释。

对于HBM文件,您必须更改实体映射文件以将Date类型映射到自定义类型:

  

如果您想了解有关如何将修复程序应用于不同Hibernate版本(或HBM文件)的更多信息,请参阅项目的github 。 您还可以在本文中阅读有关时区转换问题的更多信息。