[英]How to convert LocalDateTime to LocalDate in JPA Query?
[英]How to query JPA LocalDateTime field with a LocalDate value?
我正在尋找一種方法來獲取在某個 LocalDateTime 日期創建的對象列表,該對象保存在 Postgresql 數據庫中 TIMESTAMPTZ 類型的字段中。
為此,我嘗試使用 JpaRepository:
List<Object> findByCreationDate(LocalDate date);
但我得到了錯誤:
java.lang.IllegalArgumentException: Parameter value [2020-12-12] did not match expected type [java.time.LocalDateTime (n/a)]
我也嘗試自己編寫查詢,結果相同。
到目前為止我想到的解決方案:
獲取 Java 中的所有對象和過濾器(但我不想在那里 go,所以沒有)
轉換 LocalDateTime 中的 LocalDate 參數(但我假設在這種情況下我只會看到在同一確切時間創建的對象,所以不會。只有當我確定時間字段是午夜時,我才會考慮這個選項)。
創建一個方法findByCreationDateBetween(LocalDateTime startOfTheDay, LocalDateTime endOfTheDay)
(有趣的選項,但我想在不修改 LocalDate 並在一天的開始和結束時將其轉換為 LocalDateTime 的情況下進行查詢。)
我還嘗試找到一些能夠在查詢中“投射”LocalDate 中的 LocalDateTime 或比較 LocalDate 和 LocalDateTime 的年、月和日的函數,但沒有成功。 creationDate 的類型應保持 LocalDateTime 和 TIMESTAMPTZ 類型的數據庫字段。
是否還有其他 Jpa @Query 替代品,總體而言,這種情況下的最佳方法是什么?
您當前的解決方案很好。 但是在@Query
中,您可以使用DATE()
轉換列
@Query(value = "SELECT * FROM Entity u WHERE DATE(creation_date) = ?1", nativeQuery = true)
List<Entity> findByCreationDate(LocalDate date);
我不使用 JPA、Hibernate 或 Spring,所以我必須忽略您問題的這一方面。 我正在使用直接 JDBC 代替。
您似乎忽略了時區的關鍵問題。 對於任何給定的時刻,日期在全球范圍內因時區而異。 在某個時刻,在日本東京可能是“明天”,而在美國俄亥俄州托萊多仍然是“昨天”。
因此,當您指定從一天開始到第二天開始的日期范圍時,您必須牢記時區。
你說:
Postgresql 數據庫中 TIMESTAMPTZ 類型的字段
Postgres中的類型名稱TIMESTAMPTZ
是標准類型名稱TIMESTAMP WITH TIME ZONE
的非標准縮寫。
Postgres 將所有提交的數據存儲在 UTC 中該類型的列中,與 UTC 的偏移量為零小時-分鍾-秒。 提交的輸入隨附的任何偏移量或時區都用於首先將值調整為 UTC 以進行存儲。 存儲后,任何提交的區域/偏移信息都將被丟棄。 Postgres 以 UTC 存儲所有TIMESTAMP WITH TIME ZONE
值。
因此,如果要查詢一天內的值,必須首先在所需時區中定義一天,然后將當天的開始和結束時間調整為 UTC 值。
String input = "2020-12-12" ;
LocalDate ld = LocalDate.parse( input ) ;
確定在您選擇的時區中看到的第一個時刻。 始終讓java.time確定一天的第一時刻,永遠不要假設一天從 00:00:00 開始。 某些區域的某些日期的某些日子從其他時間開始,例如 01:00:00。
通常最好在日期時間處理中使用半開方法定義時間跨度。 開始是包容的,而結束是排斥的。 這允許跨度整齊地彼此鄰接。
因此,您的 SQL 查詢不使用BETWEEN
。 您的 SQL 將如下所示。 請注意,說“等於或晚於”的更短的方式是“不早於”(SQL 中的!<
)。
SELECT *
FROM event_
WHERE when_ !< ?
AND when_ < ?
;
您的 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[太平洋/奧克蘭]/2020-12-13T00:00+13:00[太平洋/奧克蘭]
通過提取Instant
對象來適應 UTC。 根據定義, Instant
對象始終采用 UTC。
Instant start = zdtStart.toInstant() ;
Instant end = zdtEnd.toInstant() ;
2020-12-11T11:00:00Z/2020-12-12T11:00:00Z
你的 JDBC 看起來像這樣:
myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , end ) ;
在 JDBC 代碼中,我們傳遞了Instant
對象。 這可能會或可能不會起作用,具體取決於您的 JDBC 驅動程序。 奇怪的是,JDBC 4.2 規范需要支持OffsetDateTime
但不是更常用的Instant
或ZonedDateTime
類型。 沒關系,我們可以輕松轉換。
myPreparedStatement.setObject( 1 , start.atOffset( ZoneOffset.UTC ) ) ;
myPreparedStatement.setObject( 2 , end.atOffset( ZoneOffset.UTC ) ) ;
我們可以通過調用ZonedDateTime::toOffsetDateTime
從ZonedDateTime
轉到OffsetDateTime
。 但是這些對象的偏移量不會是 UTC(零)。 您的 JDBC 驅動程序和數據庫應該能夠處理這個問題。 但出於謹慎考慮,並且為了調試和記錄,我會使用 UTC,如上所示。 但僅供參考,代碼:
myPreparedStatement.setObject( 1 , zdtStart.toOffsetDateTime() ) ;
myPreparedStatement.setObject( 2 , zdtEnd.toOffsetDateTime() ) ;
LocalDateTime
在處理時間軸上的特定點時,切勿使用LocalDateTime
。 缺少區域/偏移量的上下文, LocalDateTime
不能代表一個時刻。
使用發布后被快速刪除的答案(我必須使用列而不是屬性名稱,將查詢標記為 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);
或者,按照這個答案,我成功地測試了第二個解決方案:
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.