当前位置: 首页 > 面试题库 >

JPA使用ZoneOffset存储OffsetDateTime

萧亦
2023-03-14
问题内容

我有以下实体类:

@Entity
public class Event {
    private OffsetDateTime startDateTime;
    // ...
}

然而, 持续的,然后从与JPA 2.2结果数据库读取的实体/在信息丢失
:所述ZoneOffsetstartDateTime改变UTC(在ZoneOffset所使用的数据库的时间戳)。例如:

Event e = new Event();
e.setStartDateTime(OffsetDateTime.parse("2018-01-02T09:00-05:00"));

e.getStartDateTime().getHour(); // 9
e.getStartDateTime().getOffset(); // ZoneOffset.of("-05:00")
// ...
entityManager.persist(e); // Stored startDateTime as timestamp 2018-01-02T14:00Z

Event other = entityManager.find(Event.class, e.getId());
other.getStartDateTime().getHour(); // 14 - DIFFERENT
other.getStartDateTime().getOffset(); // ZoneOffset.of("+00:00") - DIFFERENT

我需要使用OffsetDateTime:我不能使用ZonedDateTime,因为区域规则会发生变化(无论如何,这也会遭受此信息丢失的困扰)。我无法使用LocalDateTime,因为某Event事发生在世界任何地方,ZoneOffset出于准确性原因,我需要它发生时的原始内容。我无法使用,Instant因为用户填写了事件的开始时间(事件就像约会一样)。

要求:

  • 需要能够对>, <, >=, <=, ==, !=JPA-QL中的时间戳执行比较

  • 需要能够检索与持久化的相同ZoneOffset的内容OffsetDateTime


问题答案:

//编辑:我更新了答案以反映JPA 2.1版和2.2版之间的差异。

//编辑2:添加了JPA 2.2规范链接

JPA 2.1的问题

JPA v2.1不知道Java
8类型,并将尝试对提供的值进行字符串化。对于LocalDateTime,Instant和OffsetDateTime,它将使用toString()方法并将相应的字符串保存到目标字段。

也就是说,您必须告诉JPA如何将您的值转换为相应的数据库类型,例如java.sql.Datejava.sql.Timestamp

实施并注册AttributeConverter界面以使其工作。

看到:

  • https://stuetzpunkt.wordpress.com/2015/03/15/persisting-localdate-with-jpa-2-1/
  • http://www.adam-bien.com/roller/abien/entry/new_java_8_date_and

当心亚当·比恩(Adam Bien)的错误实现:必须首先对LocalDate进行分区。

使用JPA 2.2

只是不要创建属性转换器。它们已经包括在内。

//更新2:

您可以在此处的规范中看到这一点:JPA 2.2 spec。滚动到最后一页以查看时间类型。

如果使用jpql表达式,请确保使用Instant对象,并在PDO类中也使用Instant。

例如

// query does not make any sense, probably.
query.setParameter("createdOnBefore", Instant.now());

这样很好。

使用java.time.Instant代替其他格式

无论如何,即使您具有ZonedDateTime或OffsetDateTime,从数据库读取的结果也将始终为UTC,因为数据库存储的是即时时间,而与时区无关。
时区实际上只是显示信息(元数据)

因此,我建议改为使用Instant它,并且仅在需要时将其转换为“区域”或“偏移时间”类。要恢复给定区域或偏移量的时间,请将区域或偏移量分别存储在其自己的数据库字段中。

JPQL比较将与此解决方案一起使用,只需始终保持即时操作即可。

PS:我最近与一些Spring员工进行了交谈,他们还同意,除了Instant之外,您再也不会坚持任何其他方法。只有一个瞬间是特定的时间点,然后可以使用元数据进行转换。

使用复合值

根据JPA 2.2规范,未提及CompositeValues。这意味着,他们没有将其纳入规范中,并且此时您不能将单个字段持久保存到多个数据库列中。搜索“复合”,仅看到与ID相关的提及。

但是,正如此答案的注释中所述,Hibernate可能能够做到这一点。

示例实施

我创建此示例时牢记以下原则:为扩展而打开,为修改而关闭。在此处阅读有关此原则的更多信息:Wikipedia上的开放/封闭原则。

这意味着,您可以将当前字段保留在数据库中(时间戳记),并且只需要添加其他列即可,这不会有任何问题。

另外,您的实体可以保留OffsetDateTime的设置方法和获取方法。内部结构应该与调用者无关。这意味着,此建议完全不会损害您的api。

一个实现可能看起来像这样:

@Entity
public class UserPdo {

  @Column(name = "created_on")
  private Instant createdOn;

  @Column(name = "display_offset")
  private int offset;

  public void setCreatedOn(final Instant newInstant) {
    this.createdOn = newInstant;
    this.offset = 0;
  }

  public void setCreatedOn(final OffsetDateTime dt) {
    this.createdOn = dt.toInstant();
    this.offset = dt.getOffset().getTotalSeconds();
  }

  // derived display value
  public OffsetDateTime getCreatedOnOnOffset() {
    ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(this.offset);
    return this.createdOn.atOffset(zoneOffset);
  }
}


 类似资料:
  • 问题内容: 我想知道是否可以使用批注使用JPA2 将地图持久化到以下类中 由于我们已经有一个现有的生产数据库,因此理想情况下,值 可以映射到以下现有表: 问题答案: JPA 2.0通过注释可以支持原语集合,你可以将其与集合支持一起使用。这样的事情应该起作用: 另请参见(在JPA 2.0规范中) 2.6 - Collections of Embeddable Classes and Basic Ty

  • 我正在开发一个迁移软件,它将使用来自REST服务的未知数据。 我已经考虑过使用MongoDB,但我决定不使用它,而是使用PostgreSQL。 读完本文后,我试图在我的SpringBoot应用程序中使用SpringJPA实现它,但我不知道在我的实体中映射。 我试过了,但什么也不懂! 这里就是我所在的地方: 和... 下表: 我该怎么做? 注意:我不想/不需要一个实体来工作。我的JSON将永远是St

  • 我有一个通用实体,我想使用JPA持久化,以便它的通用值存储在单个列中。简化的实体类如下所示: 类型唯一标识泛型值的类,并且允许具有相同类的多个类型: 我没有找到如何使用Hibernate保持这种泛型类的方法。我找到了替代方法和变通方法,但它们在我看来并不理想。我考虑了以下选项: > 将值的类型设置为JsonNode,并将其类型类标记为com。弗拉德米尔恰。冬眠类型json。JsonBinaryTy

  • 我正在尝试使用文本文件的内容在MySQL数据库中创建存储过程: 我使用本机查询执行SQL: 但是它在DROP PROCEDURE上得到一个错误我注释了DROP PROCEDURE,然后它在DELIMITER上得到一个错误基本上,它在第一个分号之后的任何一行上都得到一个错误。 似乎JPA hibernate正在解析我的查询并告诉我它有问题,而不是将纯文本传递到MySQL。 sql在MySQL中运行,

  • 问题内容: 我有许多需要保留到数据库的简单对象类型。我正在使用Spring JPA来管理这种持久性。对于每种对象类型,我需要构建以下内容: 在我看来,有可能用三个基于泛型的类替换每种对象类型的多个类,从而节省了大量的样板代码。我不确定该怎么做,实际上,如果这是个好主意? 问题答案: 首先,我知道我们在这里提高了一些标准,但这已经比没有Spring Data JPA的帮助而编写的代码少得多。 其次,

  • 我有一个springboot项目,为了执行请求,我正在连接到我的mysql数据库。我有一个实体ExportBatch: 我在ExportBatchRepository中定义了一个新方法: 在我的控制器里,我是这样做的: 当我调用“/GetLastBatchsProblemes”时会遇到这个异常:java.lang.ClassCastException:[Ljava.lang.Object;不能强制