簡體   English   中英

使用 JPA 和 Hibernate 時,PostgreSQL date_trunc('day', (entity.date AT TIME ZONE 'UTC')) 函數的 JPQL 等價物是什么

[英]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'))"
                )
        );
    }
}

使用 JPA persistence.xml 配置MetadataBuilderContributor

現在,您需要通過hibernate.metadata_builder_contributor配置屬性將SqlFunctionsMetadataBuilderContributor提供給 Hibernate:

<property>
    name="hibernate.metadata_builder_contributor" 
    value="com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor"
</property>

使用 Spring Boot 配置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

您可以像這樣在 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")
);

自定義傳遞給 DATE_TRUNC 的時區

如果要自定義時區,只需將時區作為參數傳遞,如下所示:

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)

來源: 如何使用 Hibernate 調用 PostgreSQL 函數?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM