环境
问题
我正在构建一个Spring微服务,它与一个不同的服务共享一个Postgresql数据库。数据库从外部初始化(超出我们的控制范围),其他服务使用的日期时间列类型是时间戳,没有时区。因此,由于我希望db上的所有日期都具有相同的类型,因此对我的JPA实体日期来说,拥有该类型是必需的。
我在JPA实体对象上映射它们的方式如下:
@Column(name = "some_date", nullable = false)
private Timestamp someDate;问题是,当我创建时间戳时,如下所示:
new java.sql.Timestamp(System.currentTimeMillis())我查看数据库,时间戳包含我的本地时区日期时间,但是我想将它存储在UTC中。这是因为我的默认时区设置为“Europe/布鲁塞尔”,JPA/JDBC在将java.sql.Timestamp对象放入数据库之前将其转换为时区。
找到了不理想的解决方案
TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));具有我想要实现的效果,但是它并不适合我的服务,因为它并不是特定于我的服务。也就是说,它将影响整个JVM或当前线程加上子线程。-Duser.timezone=GMT启动应用程序似乎也为运行中的JVM的单个实例做了工作。因此,一个比以前更好的解决方案。但是,是否有一种方法可以在JPA/数据源/spring引导配置中指定时区?
发布于 2017-01-16 12:33:10
我能找到的最合适的解决方法是使用AttributeConverter将Java8 ZonedDateTime对象转换为java.sql.Timestamp对象,这样就可以将它们映射到PostgreSQL timestamp without time zone类型。
需要AttributeConverter的原因是Java8/Joda时间时间类型为尚未与JPA兼容。。
AttributeConverter看起来如下所示:
@Converter(autoApply = true)
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zonedDateTime) {
return (zonedDateTime == null ? null : Timestamp.valueOf(zonedDateTime.toLocalDateTime()));
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime().atZone(ZoneId.of("UTC")));
}
}这使我可以读取不具有时区信息的数据库时间戳作为具有UTC时区的ZonedDateTime对象。这样,无论我的应用程序在中运行的时区是什么,我都可以在db 上看到确切的日期时间。
由于toLocalDateTime()还应用了系统的默认时区转换,因此这个AttributeConverter基本上取消了JDBC驱动程序应用的转换。
你真的需要使用timestamp without timezone吗?
现实情况是,如果您将日期时间信息存储在时区(即使是UTC),则timestamp without timezone PostgreSQL类型是错误的选择。正确的数据类型是timestamp with timezone,它确实包含时区信息。有关此主题的更多信息,这里。
但是,如果出于任何原因,必须使用timestamp without timezone,我认为上面的ZonedDateTime方法是一个健壮和一致的解决方案。
您是否也将ZonedDateTime序列化为JSON?
然后,您可能感兴趣的是,至少需要2.6.0版本的jackson-datatype-jsr310依赖才能使序列化工作。更多关于在这个答案中的报道。
发布于 2017-01-14 05:42:02
Eclipselink使用setTimestamp的单个arg版本,将时区处理的责任委托给驱动程序,postgresql驱动程序不允许覆盖默认时区。postgres驱动程序甚至将客户端时区传播到会话,因此服务器端默认设置对您也没有任何用处。
您可以尝试解决一些棘手的问题,例如编写JPA2.1 AttributeConverter将时间戳转换到目标区域,但最终它们注定会失败,因为客户端时区进行了夏时制调整,使某些时候变得模棱两可或无法表示。
您必须在客户端上设置默认时区,或者进入本机SQL,将时间戳设置为具有强制转换的字符串。
发布于 2022-06-20 11:35:05
如何使用即时,而不是LocalDateTime?我认为它可以将实体值转换为时间戳,以UTC为单位。
@Converter(autoApply = true)
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zonedDateTime) {
return (zonedDateTime == null ? null : Timestamp.from(zonedDateTime.toInstant()));
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toInstant().atZone(ZoneId.of("UTC")));
}
}https://stackoverflow.com/questions/41618165
复制相似问题