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 。 您还可以在本文中阅读有关时区转换问题的更多信息。
- 什么是与Hibernate和JDBC相关的对象/关系映射(ORM)?
- 如何在hibernate的名为key的列名中添加反引号
- JPA:将空的Fetch结果加入NULL空
- Hibernate – 将java.util.Calendar映射到MySQL BIGINT
- Hibernate中的DataBase加密
- javax.persistence.PersistenceException:org.hibernate.PersistentObjectException:传递给persist的分离实体
- 如何通过使用Spring Data调用strored过程来选择实体
- 如何使用Spring 3配置JPA?
- 在运行时获取有关entites的hibernate信息