[英]What is the JPQL equivalent of the PostgreSQL date_trunc('day', (entity.date AT TIME ZONE 'UTC')) function when using JPA and Hibernate
什么是數據庫函數date_trunc('day', (entity.date AT TIME ZONE 'UTC'))
的 JPQL/JPA/Hibernate 等價物?
我需要在 Spring Boot 應用程序中四舍五入到全天(和幾周等),Hibernate 在 Postgresql 數據庫之上運行。 這個查詢在原生 postgresql 中是可能的,但我還沒有想出如何在 Hibernate 中做到這一點。 時區(如“UTC”、“歐洲/阿姆斯特丹”)和日期部分(如“日”、“年”)可能會有所不同,因此我不能將其設置為默認值。
我在 Hibernate 論壇上發現了一個沒有任何回復的古老帖子: https : //forum.hibernate.org/viewtopic.php? f =1& t =990451
我還發現了一個相關的 StackOverflow 問題,其中海報直接想選擇某個日期作為某個時區。 但它沒有回復: HQL 相當於 Postgres 的“時區日期時間”
我的整個查詢(沒有 where)看起來像這樣,所以實現這一點的不同方式也可以。
"SELECT NEW " + AggregateQueryEntity.class.getName() +
"(date_trunc('" + aggregationPeriod.toString() +
"', a.date), a.alertConfiguration.id.id, a.alertConfiguration.name, a.alertLevel, count(*)) from Alert a" +
whereClause.toString() +
" GROUP BY 1, a.alertConfiguration.id.id, a.alertConfiguration.name, a.alertLevel" + //column 1 is the truncated date
" ORDER BY 1, alertLevel DESC"
編輯:對答案的評論。
弗拉德的回答很棒,我對其進行了更多修改,以便將日期部分也作為變量。 此外,我必須覆蓋標准的 Hibernate 行為,該行為將Date
列生成為timestamp without timezone
timestamp with timezone
通過將其放在我的列定義中來顯式地使它們timestamp with timezone
:
@Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private Date date;
如需進一步閱讀,請參閱https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29
根據 Vlad 的其他博客文章https://vladmihalcea.com/how-to-store-date-time-and-timestamps-in-utc-time ,使用 Hibernate 屬性hibernate.jdbc.time_zone
仍然是一個好主意-zone-with-jdbc-and-hibernate/並在 UTC 中啟動您的 JVM。 我也必須這樣做才能解決我所有的時區問題。
正如Hibernate User Guide 中所解釋的,你可以使用EXTRACT
函數:
List<Integer> days = entityManager
.createQuery(
"select extract( day from c.timestamp ) " +
"from Call c ", Integer.class )
.getResultList();
或day
功能:
List<Integer> days = entityManager
.createQuery(
"select day( c.timestamp ) " +
"from Call c ", Integer.class )
.getResultList();
MetadataBuilderContributor
注冊 DATE_TRUNC 函數如果您還需要使用AT TIMEZONE
,則需要像這樣注冊 PostgreSQL 函數:
public class SqlFunctionsMetadataBuilderContributor
implements MetadataBuilderContributor {
@Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"date_trunc",
new SQLFunctionTemplate(
StandardBasicTypes.TIMESTAMP,
"date_trunc('day', (?1 AT TIME ZONE 'UTC'))"
)
);
}
}
MetadataBuilderContributor
現在,您需要通過hibernate.metadata_builder_contributor
配置屬性將SqlFunctionsMetadataBuilderContributor
提供給 Hibernate:
<property>
name="hibernate.metadata_builder_contributor"
value="com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor"
</property>
MetadataBuilderContributor
如果您使用的是 Spring Boot,則需要在application.properties
文件中添加以下配置:
spring.jpa.properties.hibernate.metadata_builder_contributor=com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor
然后,假設您在數據庫中有以下Post
實體:
Post post = new Post();
post.setId(1L);
post.setTitle(
"High-Performance Java Persistence"
);
post.setCreatedOn(
Timestamp.valueOf(
LocalDateTime.of(2018, 11, 23, 11, 22, 33)
)
);
entityManager.persist(post);
您可以像這樣在 JPQL 中調用date_trunc
函數:
Tuple tuple = entityManager
.createQuery(
"select p.title as title, date_trunc(p.createdOn) as creation_date " +
"from Post p " +
"where p.id = :postId", Tuple.class)
.setParameter("postId", 1L)
.getSingleResult();
assertEquals(
"High-Performance Java Persistence",
tuple.get("title")
);
assertEquals(
Timestamp.valueOf(
LocalDateTime.of(2018, 11, 23, 0, 0, 0)
),
tuple.get("creation_date")
);
如果要自定義時區,只需將時區作為參數傳遞,如下所示:
public static class SqlFunctionsMetadataBuilderContributor
implements MetadataBuilderContributor {
@Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"date_trunc",
new SQLFunctionTemplate(
StandardBasicTypes.TIMESTAMP,
"date_trunc('day', (?1 AT TIME ZONE ?2))"
)
);
}
}
現在,您可以按如下方式調用date_trunc
函數:
Tuple tuple = entityManager
.createQuery(
"select p.title as title, date_trunc(p.createdOn, :timezone) as creation_date " +
"from Post p " +
"where p.id = :postId", Tuple.class)
.setParameter("postId", 1L)
.setParameter("timezone", "UTC")
.getSingleResult();
我在我的 high-performance-java-persistence GitHub 存儲庫中創建了以下測試用例,它們運行得很好:
您可以使用以下語法簡單地調用 postgres 函數 -
function('date_trunc', 'month', date)
舉個例子 -
select distinct w from YourModel w where studentId=:studentId and function('date_trunc', 'month', date) = :month
除了@karthiks3000 的回答,如果你使用 Hibernate 的 PostgreSQL 方言,你可以簡單地在 JPQL 查詢中調用它的函數:
date_trunc('month', date)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.