为什么JPA不支持java.time.Instant?

flseospp  于 2023-10-19  发布在  Java
关注(0)|答案(3)|浏览(157)

我认为java.time.Instant是将日期存储到DB中的最佳选择:它是最有可能的 * 时间戳 *,你不依赖于时区,它只是时间上的一个时刻。
JPA支持LocalDateLocalTimeLocalDateTime等。但不是即时的当然,你可以使用AttributeConverter或者像Jadira这样的库,但是为什么它不被支持呢?

hfsqlsce

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类型的支持。

  • 日期-本地日期
  • 时间-本地时间
  • 带外出时区的时间戳- LocalDateTime
  • 带时区的时间戳-偏移日期时间

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保持小而简单。

fafcakar

fafcakar2#

这是一个价值100万美元的问题。
为什么过去不支持,我不知道。确实很奇怪。但是,它可能会成为即将到来的JPA 3.2规范的一部分。在我看来,Instant应该是第一个支持的JSR-310类型-而不是沿着而来的最后一个。
以下是一些背景:
几乎所有的RDBMS都只能存储Instant。有些,像PostgreSQL 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的支持沿着存在。让我解释一下:

使用JDBC阅读和写入Instant值

JDBC java.sql.TimestampInstant本质上是一样的。除了由于Instant可以存储比DateTimestampDate扩展而来)更远的过去或未来的日期而导致的一些离群值之外,两者之间存在无损转换。(我们正在谈论未来年份292,469,238以及更高年份的日期,这会给你带来麻烦,并且在过去的一面也会有类似的废话,所以是的,对于所有 * 实际目的 *,两者之间存在无损转换)。
因此,剩下的工作就是向JDBC驱动程序解释我们提供的值和期望的UTC值。
假设我们有

private static final Calendar UTC_CAL = Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.UTC));

然后从列类型为TIMESTAMP的数据库中阅读Instant可以这样做:

Instant myInstant = resultSet.getTimestamp(colIdx, UTC_CAL).toInstant();

可以这样做:

preparedStatement.setTimestamp(colIdx, Timestamp.from(myInstant), UTC_CAL);

应该注意的是,上述方法是“安全的”,这意味着无论数据库服务器的默认时区设置或您自己的JVM的时区设置如何,它都将一致地工作。

ORM支持

正如你所想象的,那些在幕后支持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

你应该在JPA代码中使用Instant吗?

如前所述,Instant目前不在JPA规范中,但似乎最终会出现。有两个原因让我无论如何都愿意使用它:

  • Hibernate是Spring的默认值。是的,你可能会使用其他东西,但我敢打赌很少有人这样做。所以使用一些特定于Hibernate的东西并不会太困扰我。
  • Instant最终进入JPA规范时,我敢打赌它会像Hibernate一样简单地工作。所以没有变化。
pbpqsu0x

pbpqsu0x3#

这不是JPA问题,而是JDBC问题。
JDBC支持Date、Timestamp、LocalDate、LocalTime和LocalDateTime,但不支持Instant。
这不是Java问题,而是SQL问题,数据库中存储的是year-month-day-hour-minute-second结构。
想想SQL函数:年()月()等.
自1970年以来,这些函数不能应用于简单的毫码。它们需要LocalDateTime构造来执行这些功能。

相关问题