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

Как получить SQL-запрос из критериев JPQL или JPA

Узнайте, как можно получить автоматически сгенерированный SQL-запрос из запроса сущности JPQL или API критериев при использовании JPA и Hibernate.

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

Вступление

В этой статье я покажу вам, как вы можете получить автоматически сгенерированный SQL-запрос из запроса сущности API критериев JPQL или JPA.

Регистрация инструкций SQL

Как я объяснил в этой статье , существует множество способов регистрации инструкций SQL, созданных JPA или Hibernate.

Например, механизм JDBC Источник данных прокси-сервер позволяет перехватывать и регистрировать все выполненные инструкции. Однако даже в этом случае вам необходимо проанализировать журнал и сопоставить запросы JPA с соответствующими инструкциями SQL.

Было бы намного проще, если бы существовал инструмент, который мог бы извлекать SQL-запрос непосредственно из экземпляра JPA Query .

Проекты типов гибернации

Проект с открытым исходным кодом Hibernate Types предоставляет множество дополнений для Hibernate ORM.

В то время как большинство разработчиков знают его для дополнительных пользовательских типов, таких как JSON , МАССИВ , интервал , IP , проект Hibernate Types также предлагает множество других утилит.

Например, существует стратегия именования Hibernate, которая позволяет сопоставлять свойства сущности Java camelCase с именами столбцов snake_case.

И это еще не все. Преобразователь результатов списка позволяет преобразовать проекцию запроса по умолчанию с помощью лямбд Java 8.

Начиная с версии 2.9.11, проект Hibernate Types предлагает утилиту SQLExtractor , которая позволяет получать SQL-запрос из любого запроса JPQL или API критериев, независимо от того, используете ли вы Hibernate 5.4, 5.3, 5.2, 5.1, 5.0, 4.3, 4.2, или 4.1.

Получите инструкцию SQL из запроса JPQL

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

Query jpql = entityManager.createQuery("""
    select 
       YEAR(p.createdOn) as year, 
       count(p) as postCount 
    from 
       Post p 
    group by 
       YEAR(p.createdOn)
    """, Tuple.class
);

С типами Hibernate извлечение SQL-запроса, сгенерированного в режиме гибернации, так же просто:

String sql = SQLExtractor.from(jpql);

И, если мы зарегистрируем извлеченный SQL-запрос:

LOGGER.info("""
    The JPQL query: [
        {}
    ]
    generates the following SQL query: [ 
        {}
    ]
    """,
    jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

Мы получаем следующий результат:

- The JPQL query: [
    select    
        YEAR(p.createdOn) as year,    
        count(p) as postCount 
    from    
        Post p 
    group by    
        YEAR(p.createdOn)
]
generates the following SQL query: [
    SELECT 
        extract(YEAR FROM sqlextract0_.created_on) AS col_0_0_,
        count(sqlextract0_.id) AS col_1_0_
    FROM 
        post p
    GROUP BY 
        extract(YEAR FROM p.created_on)
]

Блестяще!

Обратите внимание, что мы развернули JPA Запрос в режим гибернации org.hibernate.запрос.Интерфейс запроса , который предоставил метод getQueryString , который мы можем использовать для регистрации связанной строки запроса JPQL.

Получите инструкцию SQL из запроса API критериев JPA

SQL-экстрактор не ограничивается запросами JPQL. Вы также можете использовать его с запросами API критериев, как показано в следующем примере:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery criteria = builder.createQuery(PostComment.class);

Root postComment = criteria.from(PostComment.class);
Join post = postComment.join("post");

criteria.where(
    builder.like(post.get("title"), "%Java%")
);

criteria.orderBy(
    builder.asc(postComment.get("id"))
);

Query criteriaQuery = entityManager.createQuery(criteria);

String sql = SQLExtractor.from(criteriaQuery);

assertNotNull(sql);

LOGGER.info("""
    The Criteria API, compiled to this JPQL query: [
        {}
    ]
    generates the following SQL query: [
        {}
    ]
    """,
    criteriaQuery.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

При выполнении приведенного выше тестового случая мы получаем следующий SQL-запрос:

- The Criteria API, compiled to this JPQL query: [
    select 
        pc 
    from 
        PostComment as pc 
    inner join 
        pc.post as p 
    where 
        p.title like :param0 
    order by 
        pc.id asc
]
generates the following SQL query: [
    SELECT 
        pc.id AS id1_1_,
        pc.post_id AS post_id3_1_,
        pc.review AS review2_1_
    FROM 
        post_comment pc
    INNER JOIN 
        post p ON pc.post_id=p.id
    WHERE 
        p.title LIKE ?
    ORDER BY 
        pc.id ASC
]

API критериев сначала компилируется в запрос JPQL, как показано в вызове метода getQueryString () .

Промежуточный запрос JPQL далее преобразуется в SQL-запрос, который правильно решается утилитой SQL Extractor .

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

Вывод

Получение SQL – запроса, связанного с запросом API критериев JPQL или JPA, является очень полезной функцией. И вы можете использовать для проверки автоматически сгенерированных SQL-запросов даже без запуска JPA Запроса .

Хотя вы также можете извлечь SQL-запрос из журнала приложений, SQLExtractor позволяет объединить JPA и SQL-запросы, чтобы создать сообщение журнала, содержащее оба запроса. И, если вы используете механизм медленного ведения журнала запросов, вы можете затем сопоставить запрос JPQL или API критериев, который сгенерировал конкретный медленный SQL-запрос.