[英]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:到目前为止我想到的解决方案:
To get all the objects and filter in Java (but I wouldn't want to go there, so no)获取 Java 中的所有对象和过滤器(但我不想在那里 go,所以没有)
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 参数(但我假设在这种情况下我只会看到在同一确切时间创建的对象,所以不会。只有当我确定时间字段是午夜时,我才会考虑这个选项)。
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 代替。
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.因此,当您指定从一天开始到第二天开始的日期范围时,您必须牢记时区。
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
但不是更常用的Instant
或ZonedDateTime
类型。 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::toOffsetDateTime
从ZonedDateTime
转到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() ) ;
LocalDateTime
for momentsLocalDateTime
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
不能代表一个时刻。
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.