我想在SQL Server 2008中使用datetimeoffset列来存储日期和时间。时间加上时区。
数据库中列的DDL定义:
LastUpdatedDateTime datetimeoffset DEFAULT SysDateTimeOffset()
使用Hibernate映射我的实体上的字段的Java定义:
private ZonedDateTime lastUpdatedDateTime = ZonedDateTime.now();
我正在使用Hibernate 5.1.0.Final(通过Spring引导1.3.2.RELEASE)并包含org.hibernate:hibernate-java8。
观察到的行为(通过使用WinSQL查询数据库): 通过SQL插入语句插入数据会导致存储正确的日期和时间并更正时区:2016-03-03 13:41:17.5358944 -07:00
通过保存Java实体插入数据(根据上面的Java代码片段初始化字段)。 Java将日期/时间值(保存前)报告为:2016-03-04T14:18:17.076-07:00 [America / Denver]
保存后,WinSQL会将存储在数据库中的值报告为:2016-03-04 14:18:17.0760000 +00:00
这个日期和时间相同,但错误的时区(UTC而不是-07:00)。
当我使用Timestamp而不是ZonedDateTime在Java中声明字段时,我得到了相同的行为。
如何正确存储时区?我不关心它是否存储为UTC或-07:00时区,只要时间基于时区是正确的。我认为Hibernate将为此提供支持(在hibernate-java8库中)并且我不必编写自定义转换器或自定义用户数据类型的代码。
答案 0 :(得分:6)
我终于找到了解决方案:
对实体字段使用java.time.OffsetDateTime而不是ZonedDateTime。根据类Javadoc,OffsetDateTime旨在用于数据库持久性。
恢复到Hibernate 4(由于我遇到的其他问题)。因此,如果您将Hibernate 5与hibernate-java8库一起使用,我不知道下一步是否必要。
将OffsetDateTime中的Hibernate转换器添加到String。显然,JDBC默认将datetimeoffset列视为String(而不是Microsoft JDBC驱动程序文档中建议的microsoft.sql.DateTimeOffset类)。此转换器中的逻辑必须处理SQL Server仅存储7位数纳秒的复杂性,而OffsetDateTime提供9位。
确保转换器包含在Hibernate EntityManager中。
这些步骤的详细信息如下:
DDL列定义未更改。
实体字段定义:
private OffsetDateTime lastUpdatedDateTime;
转换器类:
@Converter(autoApply = true)
public class OffsetDateTimeConverter implements AttributeConverter<OffsetDateTime, String> {
private static DateTimeFormatter FORMATTER_FROM_DB = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.nnnnnnn xxx");
private static DateTimeFormatter FORMATTER_TO_DB = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.nnnnnnnnn xxx");
@Override
public String convertToDatabaseColumn(OffsetDateTime attribute) {
if (attribute == null) {
return null;
}
return attribute.format(FORMATTER_TO_DB);
}
@Override
public OffsetDateTime convertToEntityAttribute(String dbData) {
if (dbData == null) {
return null;
}
return OffsetDateTime.parse(dbData, FORMATTER_FROM_DB);
}
}