Рубрики
Без рубрики

Как выполнять функции SQL с несколькими параметрами в запросе JPQL с помощью Hibernate

Узнайте, как выполнять функции SQL с несколькими параметрами при использовании запросов сущностей, таких как JPQL или API критериев, с помощью Hibernate.

Автор оригинала: Vlad Mihalcea.

В этой статье мы рассмотрим, как вы можете выполнять функции SQL с несколькими параметрами в запросах JPQL с помощью Hibernate. Недавно я наткнулся на это требование, отвечая на этот вопрос StackOverflow , который является нескончаемым источником вдохновения для статей в моем блоге.

Давайте рассмотрим, что мы используем следующую сущность Post в нашем приложении:

Объект Post отображается следующим образом:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @Column(name = "created_on")
    private Timestamp createdOn;

    //Getters and setters omitted for brevity
}

Наш бизнес-вариант использования требует, чтобы мы вызывали функцию DATE_TRUNC PostgreSQL следующим образом:

SELECT 
    p.title AS col_0_0_,
    date_trunc(
        'day', 
        (p.created_on AT TIME ZONE ?)
    ) AS col_1_0_
FROM 
    post p
WHERE 
    p.id = ?

Как объясняется в этой статье , перед использованием функции SQL в предложении SELECT запроса JPQL или API критериев нам сначала необходимо зарегистрировать эту функцию.

Лучший способ зарегистрировать функцию SQL в Hibernate-предоставить Конструктор метаданных , как показано ниже:

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 ?2))"
            )
        );
    }
}

Теперь вам нужно указать Hibernate использовать Конструктор метаданных функций Sql с помощью свойства конфигурации hibernate.metadata_builder_contributor|/.

Вы можете либо добавить свойство hibernate.metadata_builder_contributor в JPA persistence.xml файл:


    name="hibernate.metadata_builder_contributor" 
    value="com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor"

Или, если вы используете Spring Boot, вы можете добавить следующую запись в файл конфигурации application.properties :

spring.jpa.properties.hibernate.metadata_builder_contributor=com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor

Теперь, предполагая, что вы сохранили следующую Запись сущность:

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);

Теперь вы можете использовать функцию DATE_TRUNC SQL в запросе JPQL, подобном этому:

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();

assertEquals(
    "High-Performance Java Persistence", 
    tuple.get("title")
);

assertEquals(
    Timestamp.valueOf(
        LocalDateTime.of(2018, 11, 23, 0, 0, 0)
    ), 
    tuple.get("creation_date")
);

И Hibernate собирается выполнить инструкцию SQL, которую мы хотели с самого начала:

Query:["
SELECT 
    p.title AS col_0_0_,
    date_trunc(
        'day', 
        (p.created_on AT TIME ZONE ?)
    ) AS col_1_0_
FROM post p
WHERE p.id=?
"], 
Params:[(
    UTC, 
    1
)]

Круто, правда?

Использование свойства конфигурации hibernate.metadata_builder_contributor – лучший способ зарегистрировать функцию SQL в Hibernate, и вы можете параметризовать функцию, чтобы передать ей свойства сущности и даже стандартные параметры запроса, как в случае параметра UTC , который мы использовали при вызове функции DATE_TRUNCT .