我认为java.time.Instant是将日期存储到DB中的最佳选择:它是最有可能的 * 时间戳 *,你不依赖于时区,它只是时间上的一个时刻。JPA支持LocalDate、LocalTime、LocalDateTime等。但不是即时的当然,你可以使用AttributeConverter或者像Jadira这样的库,但是为什么它不被支持呢?
java.time.Instant
LocalDate
LocalTime
LocalDateTime
AttributeConverter
hfsqlsce1#
我再试一次。在the issue中有一些讨论。最新的讨论似乎是:mkarg说:虽然这是绝对正确的,但技术上的答案有点复杂:使数据类型有资格包含在强制类型Map集中的最终 predicate 是什么?可以说,这个 predicate 是“必不可少的”或“共同使用的”,但谁定义了什么是“必不可少的”或“共同使用的”?对于某些应用程序,对java.awt.Image和java.NET.URL的支持可能比对LocalDate或ZonedDateTime的支持更重要。另一方面,其他应用程序可能充满了LocalDate,但从不使用Instant。那么,到底在哪里进行切割呢?当查看JRE中发现的大量类型时,这变得特别复杂,很明显必须在某个地方进行削减。即使是与JRE捆绑在一起的JavaFX,在v8中也不支持Instant,那么为什么JPA应该支持呢?看看Project Jigsaw的当前进度,可能限定 predicate 可以简单地回答为“特定Jigsaw模块中的所有类型”?不管怎么说,这不是我能决定的。我确实支持你的请求,并且希望看到对所有Java Time API时间的支持,特别是对Instant和Duration的支持,你的请求有突出的支持者,比如我最近了解到的Java Champion Arun Gupa。但我怀疑最终的答案是否会像我们所希望的那样简单而令人满意。也许最好是简单地设置另一个JSR,比如“Java平台的公共数据类型转换”,它提供了比日期和时间更多的Map,但也不会绑定到JPA,但也可以被JAXB,JAX-RS使用,可能还有更多的API处理转换“到“的问题?拥有这样一辆车真的会减少很多样板。
**TL-DR;有很多类型。我们必须在某个地方划清界限。
有a new issue可以添加到未来的JPA版本中。我在Douglas Surber上发现的另一个有趣的分析(适用于JDBC):JDBC的JDK 8版本包括对与310类对应的大多数SQL类型的支持。
JDK 8版本的JDBC不包括INTERVAL类型和相应的310类之间的Map。没有SQL类型与任何其他310类完全对应。因此,JDBC规范对所有其他类都是沉默的。我强烈建议JDBC开发人员使用新的310类。java.util.Date、java.sql.Date、java.sql.Time和java. sql. Timestamp存在问题。你应该把它们看作是过时的。310班是非常优越的上级。道格拉斯
TL:DR;我们只是为您可能在数据库中存储时态数据的4种可能方式中的每一种选择了一种Java 8类型。
最后,如果你通读this thread,你会发现有很大的文化压力要求标准API保持小而简单。
fafcakar2#
这是一个价值100万美元的问题。为什么过去不支持,我不知道。确实很奇怪。但是,它可能会成为即将到来的JPA 3.2规范的一部分。在我看来,Instant应该是第一个支持的JSR-310类型-而不是沿着而来的最后一个。以下是一些背景:几乎所有的RDBMS都只能存储Instant。有些,像PostgreSQL timestamptz,会给你给予他们可以做更多事情的 * 错觉 *,但正如许多其他人指出的那样,这实际上是一个骗局。如果你的实体类看起来像这样
Instant
timestamptz
@Entity public class MyEntity { ... @Column(name="created_at") private ZonedDateTime createdAt; public ZonedDateTime getCreatedAt() { return this.createdAt; } public void setCreatedAt(ZonedDateTime ts) { this.createdAt = ts; } }
你很可能做错了**:这样,持久层代码就给您一种错觉,以为它可以为您存储ZonedDateTime,而实际上它不能。(值得注意的例外是Oracle数据库,它实际上可以存储ZonedDateTime而不会丢失信息,但我必须承认我从未见过它在真实的生活中使用)。我发现了这个评论:JDBC没有涵盖Instant,JPA也没有。至少现在还没有这可能解释了为什么JPA维护者直到最近才承认为什么应该在JPA规范中提到它。但上述说法是错误的。JDBC中对Instant的支持沿着存在。让我解释一下:
ZonedDateTime
JDBC java.sql.Timestamp和Instant本质上是一样的。除了由于Instant可以存储比Date(Timestamp从Date扩展而来)更远的过去或未来的日期而导致的一些离群值之外,两者之间存在无损转换。(我们正在谈论未来年份292,469,238以及更高年份的日期,这会给你带来麻烦,并且在过去的一面也会有类似的废话,所以是的,对于所有 * 实际目的 *,两者之间存在无损转换)。因此,剩下的工作就是向JDBC驱动程序解释我们提供的值和期望的UTC值。假设我们有
java.sql.Timestamp
Date
Timestamp
private static final Calendar UTC_CAL = Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.UTC));
然后从列类型为TIMESTAMP的数据库中阅读Instant可以这样做:
TIMESTAMP
Instant myInstant = resultSet.getTimestamp(colIdx, UTC_CAL).toInstant();
而写可以这样做:
preparedStatement.setTimestamp(colIdx, Timestamp.from(myInstant), UTC_CAL);
应该注意的是,上述方法是“安全的”,这意味着无论数据库服务器的默认时区设置或您自己的JVM的时区设置如何,它都将一致地工作。
正如你所想象的,那些在幕后支持Instant的ORM完全是如上所述。Hibernate和Nucleus都支持Instant,但还没有支持。你的实体类应该看起来像这样:
@Entity public class MyEntity { ... @Column(name="created_at") private Instant createdAt; // getters and setters }
在使用Hibernate时,你可以在互联网上找到许多关于必须将hibernate.jdbc.time_zone设置为UTC的故事。这在上面的代码中是没有必要的,至少在Hibernate 6中是没有必要的。原因是Hibernate可以看到你的意图(你指定了Instant,而不是任何其他JSR-310类型),所以它知道当从/向数据库阅读值时,它必须使用静态UTC_CAL。
hibernate.jdbc.time_zone
UTC
UTC_CAL
如前所述,Instant目前不在JPA规范中,但似乎最终会出现。有两个原因让我无论如何都愿意使用它:
pbpqsu0x3#
这不是JPA问题,而是JDBC问题。JDBC支持Date、Timestamp、LocalDate、LocalTime和LocalDateTime,但不支持Instant。这不是Java问题,而是SQL问题,数据库中存储的是year-month-day-hour-minute-second结构。想想SQL函数:年()月()等.自1970年以来,这些函数不能应用于简单的毫码。它们需要LocalDateTime构造来执行这些功能。
3条答案
按热度按时间hfsqlsce1#
我再试一次。在the issue中有一些讨论。最新的讨论似乎是:
mkarg说:虽然这是绝对正确的,但技术上的答案有点复杂:使数据类型有资格包含在强制类型Map集中的最终 predicate 是什么?
可以说,这个 predicate 是“必不可少的”或“共同使用的”,但谁定义了什么是“必不可少的”或“共同使用的”?对于某些应用程序,对java.awt.Image和java.NET.URL的支持可能比对LocalDate或ZonedDateTime的支持更重要。另一方面,其他应用程序可能充满了LocalDate,但从不使用Instant。那么,到底在哪里进行切割呢?当查看JRE中发现的大量类型时,这变得特别复杂,很明显必须在某个地方进行削减。即使是与JRE捆绑在一起的JavaFX,在v8中也不支持Instant,那么为什么JPA应该支持呢?看看Project Jigsaw的当前进度,可能限定 predicate 可以简单地回答为“特定Jigsaw模块中的所有类型”?
不管怎么说,这不是我能决定的。我确实支持你的请求,并且希望看到对所有Java Time API时间的支持,特别是对Instant和Duration的支持,你的请求有突出的支持者,比如我最近了解到的Java Champion Arun Gupa。但我怀疑最终的答案是否会像我们所希望的那样简单而令人满意。
也许最好是简单地设置另一个JSR,比如“Java平台的公共数据类型转换”,它提供了比日期和时间更多的Map,但也不会绑定到JPA,但也可以被JAXB,JAX-RS使用,可能还有更多的API处理转换“到“的问题?拥有这样一辆车真的会减少很多样板。
**TL-DR;有很多类型。我们必须在某个地方划清界限。
有a new issue可以添加到未来的JPA版本中。
我在Douglas Surber上发现的另一个有趣的分析(适用于JDBC):
JDBC的JDK 8版本包括对与310类对应的大多数SQL类型的支持。
JDK 8版本的JDBC不包括INTERVAL类型和相应的310类之间的Map。
没有SQL类型与任何其他310类完全对应。因此,JDBC规范对所有其他类都是沉默的。
我强烈建议JDBC开发人员使用新的310类。java.util.Date、java.sql.Date、java.sql.Time和java. sql. Timestamp存在问题。你应该把它们看作是过时的。310班是非常优越的上级。
道格拉斯
TL:DR;我们只是为您可能在数据库中存储时态数据的4种可能方式中的每一种选择了一种Java 8类型。
最后,如果你通读this thread,你会发现有很大的文化压力要求标准API保持小而简单。
fafcakar2#
这是一个价值100万美元的问题。
为什么过去不支持,我不知道。确实很奇怪。但是,它可能会成为即将到来的JPA 3.2规范的一部分。在我看来,
Instant
应该是第一个支持的JSR-310类型-而不是沿着而来的最后一个。以下是一些背景:
几乎所有的RDBMS都只能存储Instant。有些,像PostgreSQL
timestamptz
,会给你给予他们可以做更多事情的 * 错觉 *,但正如许多其他人指出的那样,这实际上是一个骗局。如果你的实体类看起来像这样
你很可能做错了**:这样,持久层代码就给您一种错觉,以为它可以为您存储
ZonedDateTime
,而实际上它不能。(值得注意的例外是Oracle数据库,它实际上可以存储ZonedDateTime而不会丢失信息,但我必须承认我从未见过它在真实的生活中使用)。我发现了这个评论:
JDBC没有涵盖Instant,JPA也没有。至少现在还没有
这可能解释了为什么JPA维护者直到最近才承认为什么应该在JPA规范中提到它。但上述说法是错误的。JDBC中对
Instant
的支持沿着存在。让我解释一下:使用JDBC阅读和写入Instant值
JDBC
java.sql.Timestamp
和Instant
本质上是一样的。除了由于Instant
可以存储比Date
(Timestamp
从Date
扩展而来)更远的过去或未来的日期而导致的一些离群值之外,两者之间存在无损转换。(我们正在谈论未来年份292,469,238以及更高年份的日期,这会给你带来麻烦,并且在过去的一面也会有类似的废话,所以是的,对于所有 * 实际目的 *,两者之间存在无损转换)。因此,剩下的工作就是向JDBC驱动程序解释我们提供的值和期望的UTC值。
假设我们有
然后从列类型为
TIMESTAMP
的数据库中阅读Instant
可以这样做:而写可以这样做:
应该注意的是,上述方法是“安全的”,这意味着无论数据库服务器的默认时区设置或您自己的JVM的时区设置如何,它都将一致地工作。
ORM支持
正如你所想象的,那些在幕后支持
Instant
的ORM完全是如上所述。Hibernate和Nucleus都支持Instant
,但还没有支持。你的实体类应该看起来像这样:
在使用Hibernate时,你可以在互联网上找到许多关于必须将
hibernate.jdbc.time_zone
设置为UTC
的故事。这在上面的代码中是没有必要的,至少在Hibernate 6中是没有必要的。原因是Hibernate可以看到你的意图(你指定了Instant
,而不是任何其他JSR-310类型),所以它知道当从/向数据库阅读值时,它必须使用静态UTC_CAL
。你应该在JPA代码中使用
Instant
吗?如前所述,
Instant
目前不在JPA规范中,但似乎最终会出现。有两个原因让我无论如何都愿意使用它:Instant
最终进入JPA规范时,我敢打赌它会像Hibernate一样简单地工作。所以没有变化。pbpqsu0x3#
这不是JPA问题,而是JDBC问题。
JDBC支持Date、Timestamp、LocalDate、LocalTime和LocalDateTime,但不支持Instant。
这不是Java问题,而是SQL问题,数据库中存储的是year-month-day-hour-minute-second结构。
想想SQL函数:年()月()等.
自1970年以来,这些函数不能应用于简单的毫码。它们需要LocalDateTime构造来执行这些功能。