简体   繁体   English

如何使用 LocalDate 值查询 JPA LocalDateTime 字段?

[英]How to query JPA LocalDateTime field with a LocalDate value?

I search for a way to get a list of Objects created on a certain LocalDateTime date saved in the Postgresql database in a field of type TIMESTAMPTZ.我正在寻找一种方法来获取在某个 LocalDateTime 日期创建的对象列表,该对象保存在 Postgresql 数据库中 TIMESTAMPTZ 类型的字段中。

To do so, I tried to use JpaRepository:为此,我尝试使用 JpaRepository:

List<Object> findByCreationDate(LocalDate date);

But I get the error:但我得到了错误:

java.lang.IllegalArgumentException: Parameter value [2020-12-12] did not match expected type [java.time.LocalDateTime (n/a)]

I also tried writing the query myself with the same result.我也尝试自己编写查询,结果相同。

The solutions I thought about so far:到目前为止我想到的解决方案:

  1. To get all the objects and filter in Java (but I wouldn't want to go there, so no)获取 Java 中的所有对象和过滤器(但我不想在那里 go,所以没有)

  2. To convert the LocalDate parameter in LocalDateTime (but I assume in this case I will see only Objects created at the same exact time, so no. I would consider this option only when I'm sure the fields of time are midnight).转换 LocalDateTime 中的 LocalDate 参数(但我假设在这种情况下我只会看到在同一确切时间创建的对象,所以不会。只有当我确定时间字段是午夜时,我才会考虑这个选项)。

  3. To create a method findByCreationDateBetween(LocalDateTime startOfTheDay, LocalDateTime endOfTheDay) (interesting option, but I would like to do the query without modifying the LocalDate and converting it to LocalDateTime at the start and at the end of the day.)创建一个方法findByCreationDateBetween(LocalDateTime startOfTheDay, LocalDateTime endOfTheDay) (有趣的选项,但我想在不修改 LocalDate 并在一天的开始和结束时将其转换为 LocalDateTime 的情况下进行查询。)

I also tried to find some functions that would be able to 'cast' the LocalDateTime in LocalDate in the query or compare the year, the month and the day of the LocalDate and LocalDateTime, but unsuccessfully.我还尝试找到一些能够在查询中“投射”LocalDate 中的 LocalDateTime 或比较 LocalDate 和 LocalDateTime 的年、月和日的函数,但没有成功。 The type of creationDate should remain LocalDateTime and the database field of type TIMESTAMPTZ. creationDate 的类型应保持 LocalDateTime 和 TIMESTAMPTZ 类型的数据库字段。

Are there any other Jpa @Query alternatives and overall, what would be the best approach for this case?是否还有其他 Jpa @Query 替代品,总体而言,这种情况下的最佳方法是什么?

Your current solution is fine.您当前的解决方案很好。 But in @Query you can cast column using DATE()但是在@Query中,您可以使用DATE()转换列

@Query(value = "SELECT * FROM Entity u WHERE DATE(creation_date) = ?1", nativeQuery = true)
List<Entity> findByCreationDate(LocalDate date);

I do not use JPA, Hibernate, or Spring, so I must ignore that aspect of your Question.我不使用 JPA、Hibernate 或 Spring,所以我必须忽略您问题的这一方面。 I am using straight JDBC instead.我正在使用直接 JDBC 代替。

A day's range of moments requires a time zone一天的时间范围需要一个时区

You seem to be ignoring the crucial issue of time zone.您似乎忽略了时区的关键问题。 For any given moment, the date varies around the globe by time zone.对于任何给定的时刻,日期在全球范围内因时区而异。 At some moment, it may be "tomorrow" in Tokyo Japan while still "yesterday" in Toledo Ohio US.在某个时刻,在日本东京可能是“明天”,而在美国俄亥俄州托莱多仍然是“昨天”。

So you must have a time zone in mind when you specify the range of a date from start of day to start of the following day.因此,当您指定从一天开始到第二天开始的日期范围时,您必须牢记时区。

Convert from zoned time to UTC for search criteria从分区时间转换为 UTC 以获取搜索条件

You said:你说:

Postgresql database in a field of type TIMESTAMPTZ Postgresql 数据库中 TIMESTAMPTZ 类型的字段

The type name TIMESTAMPTZ in Postgres is a non-standard abbreviation for the standard type name TIMESTAMP WITH TIME ZONE . Postgres中的类型名称TIMESTAMPTZ是标准类型名称TIMESTAMP WITH TIME ZONE的非标准缩写。

Postgres stores all submitted data in a column of that type in UTC, with an offset of zero hours-minutes-seconds from UTC. Postgres 将所有提交的数据存储在 UTC 中该类型的列中,与 UTC 的偏移量为零小时-分钟-秒。 Any offset or time zone accompanying submitted inputs is used to first adjust the value to UTC for storage.提交的输入随附的任何偏移量或时区都用于首先将值调整为 UTC 以进行存储。 After storage, any submitted zone/offset info is discarded.存储后,任何提交的区域/偏移信息都将被丢弃。 Postgres stores all TIMESTAMP WITH TIME ZONE values in UTC. Postgres 以 UTC 存储所有TIMESTAMP WITH TIME ZONE值。

So if you want to query for values that lay within the duration of a day, you must first define a day in your desired time zone, then adjust the start and end of that day into UTC values.因此,如果要查询一天内的值,必须首先在所需时区中定义一天,然后将当天的开始和结束时间调整为 UTC 值。

String input = "2020-12-12" ;
LocalDate ld = LocalDate.parse( input ) ;

Determine the first moment as seen in the time zone of your choice.确定在您选择的时区中看到的第一个时刻。 Always let java.time determine first moment of the day, never assume a day starts at 00:00:00.始终让java.time确定一天的第一时刻,永远不要假设一天从 00:00:00 开始。 Some days on some dates in some zones start at other times such as 01:00:00.某些区域的某些日期的某些日子从其他时间开始,例如 01:00:00。

Usually best in date-time handling to define a span-of-time using Half-Open approach.通常最好在日期时间处理中使用半开方法定义时间跨度。 The beginning is inclusive , while the ending is exclusive .开始是包容的,而结束是排斥的。 This allows spans to neatly abut one another.这允许跨度整齐地彼此邻接。

So your SQL query does not use BETWEEN .因此,您的 SQL 查询使用BETWEEN Your SQL will look something like the following.您的 SQL 将如下所示。 Note that a shorter way to say "is equal to or later than" is "is not before" ( !< in SQL).请注意,说“等于或晚于”的更短的方式是“不早于”(SQL 中的!< )。

SELECT * 
FROM event_
WHERE when_ !< ? 
AND when_ < ? 
;

Your Java looks like this.您的 Java 看起来像这样。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
ZonedDateTime zdtEnd = ld.plusDays( 1 ).atStartOfDay( z ) ;

2020-12-12T00:00+13:00[Pacific/Auckland]/2020-12-13T00:00+13:00[Pacific/Auckland] 2020-12-12T00:00+13:00[太平洋/奥克兰]/2020-12-13T00:00+13:00[太平洋/奥克兰]

Adjust to UTC by extracting Instant objects.通过提取Instant对象来适应 UTC。 Instant objects are always in UTC, by definition.根据定义, Instant对象始终采用 UTC。

Instant start = zdtStart.toInstant() ;
Instant end = zdtEnd.toInstant() ;

2020-12-11T11:00:00Z/2020-12-12T11:00:00Z 2020-12-11T11:00:00Z/2020-12-12T11:00:00Z

See this code run live at IdeOne.com .请参阅在 IdeOne.com 上实时运行的代码

And your JDBC looks like this:你的 JDBC 看起来像这样:

myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , end ) ;

In that JDBC code, we are passing Instant objects.在 JDBC 代码中,我们传递了Instant对象。 That may or may not work, depending on your JDBC driver.这可能会或可能不会起作用,具体取决于您的 JDBC 驱动程序。 Oddly, the JDBC 4.2 spec requires support for OffsetDateTime but not the more commonly used Instant or ZonedDateTime types.奇怪的是,JDBC 4.2 规范需要支持OffsetDateTime但不是更常用的InstantZonedDateTime类型。 No matter, we can easily convert.没关系,我们可以轻松转换。

myPreparedStatement.setObject( 1 , start.atOffset( ZoneOffset.UTC ) ) ;
myPreparedStatement.setObject( 2 , end.atOffset( ZoneOffset.UTC ) ) ;

And we could have gone from ZonedDateTime to OffsetDateTime by calling ZonedDateTime::toOffsetDateTime .我们可以通过调用ZonedDateTime::toOffsetDateTimeZonedDateTime转到OffsetDateTime But then the offset of those objects would not be UTC (zero).但是这些对象的偏移量不会是 UTC(零)。 Your JDBC driver and database should be able to handle that.您的 JDBC 驱动程序和数据库应该能够处理这个问题。 But out of an abundance of caution, and for the sake of debugging and logging, I would use UTC as seen above.但出于谨慎考虑,并且为了调试和记录,我会使用 UTC,如上所示。 But FYI, the code:但仅供参考,代码:

myPreparedStatement.setObject( 1 , zdtStart.toOffsetDateTime() ) ;
myPreparedStatement.setObject( 2 , zdtEnd.toOffsetDateTime() ) ;

Never use LocalDateTime for moments永远不要使用LocalDateTime

Never use LocalDateTime when dealing with moments, with specific points on the timeline.在处理时间轴上的特定点时,切勿使用LocalDateTime Lacking the context of a zone/offset, a LocalDateTime cannot represent a moment.缺少区域/偏移量的上下文, LocalDateTime不能代表一个时刻。


Java(传统和现代)和标准 SQL 中的日期时间类型表

Using an answer that was quickly deleted after it was posted (I had to use the column and not the property name, mark the query as nativeQuery and also select with '.*' to avoid org.postgresql.util.PSQLException: The column name id was not found in this ResultSet error), I found this solution:使用发布后被快速删除的答案(我必须使用列而不是属性名称,将查询标记为 nativeQuery 以及 select 与 '.*' 以避免org.postgresql.util.PSQLException: The column name id was not found in this ResultSet error),我找到了这个解决方案:

@Query(value = "SELECT e.* FROM Entity e WHERE DATE(creation_date) =:date", nativeQuery = true)
List<Entity> findByCreationDate(LocalDate date);

Alternatively, following this answer , I tested with success a second solution:或者,按照这个答案,我成功地测试了第二个解决方案:

default List<Entity> findByCreationDate(LocalDate localDate) {
    return findByPublicationDateBetween(localDate.atStartOfDay(), localDate.plusDays(1).atStartOfDay());
}


List<Entity> findByCreationDateBetween(LocalDateTime from, LocalDateTime to);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 JPA 查询中将 LocalDateTime 转换为 LocalDate? - How to convert LocalDateTime to LocalDate in JPA Query? 如何使用 LocalDate 查询 LocalDateTime? - How to query LocalDateTime with LocalDate? 如何指定一个字段,以便在查询时使用字段的Joda LocalDate或LocalDateTime值将其映射到同一列? - How to specify a field, so that when we query, using Joda LocalDate or LocalDateTime value for the field, it is mapped to same column? Spring JPA 实体映射 LocalDate 和 LocalDateTime 字段到 Sql 服务器 - Spring JPA entity mapping LocalDate and LocalDateTime field to Sql Server JPA-QL 查询查找在 LocalDate startDate 和 LocalDate endDate 之间具有 LocalDateTime 时间戳的所有实体 - JPA-QL query find all entities with LocalDateTime timestamp between LocalDate startDate and LocalDate endDate 我如何使用 REST controller GET 将 LocalDateTime 反序列化为 LocalDate,其中响应值为 LocalDateTime,DTO 中的值为 LocalDate? - How i can deserialize LocalDateTime into LocalDate using a REST controller GET where value in response is LocalDateTime, value in DTO is LocalDate? Spring:从@Value 参数创建 LocalDate 或 LocalDateTime - Spring: create LocalDate or LocalDateTime from @Value parameter 如何使用类 LocalDate 和 LocalDateTime 的抽象? - How to use abstraction with classes LocalDate and LocalDateTime? 如何从 LocalDate 和 LocalDateTime 中提取纪元? - How to extract epoch from LocalDate and LocalDateTime? 如何使用 JPA 持久化 LocalDate? - How to persist LocalDate with JPA?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM