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

Стойкость Blaze – лучший способ написания критериев JPA запросы

Узнайте, почему платформа сохраняемости Blaze предоставляет лучший API для написания запросов по критериям JPA, использующих множество расширенных функций SQL.

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

Вступление

В этой статье я собираюсь объяснить, почему платформа Blaze Persistence предоставляет лучший API для написания запросов по критериям JPA.

Blaze Persistence-это очень продвинутая система доступа к данным , поддерживаемая Кристианом Бейковым , который также является Участник проекта Hibernate ORM Blaze Persistence is a very advanced data access framework maintained by Christian Beikov

Я впервые услышал о постоянстве Blaze во время интервью , которое Кристиан дал нам для форума Hibernate , и если вы используете JPA и Hibernate, вам определенно следует также использовать постоянство Blaze.

Модель предметной области

Давайте предположим, что у нас есть родительская post таблица, которая имеет отношение один ко многим таблицам с post_comment дочерней таблицей.

Таблица post содержит одну запись:

| id | title                             |
|----|-----------------------------------|
| 1  | High-Performance Java Persistence |

И post_comment дочерняя таблица содержит три строки, которые связаны с одной записью post :

| id | review                                | post_id |
|----|---------------------------------------|---------|
| 1  | Best book on JPA and Hibernate!       | 1       |
| 2  | A great reference book.               | 1       |
| 3  | A must-read for every Java developer! | 1       |

Получение поста с последним комментарием

Теперь нам нужен отчет, который предоставит нам информацию о посте вместе с его последней версией post_comment , и для этой цели мы можем использовать следующий SQL-запрос:

SELECT
   p.id AS post_id,
   p.title AS post_title,
   pc2.review AS comment_review
FROM (
   SELECT
      pc1.id AS id,
      pc1.review AS review,
      pc1.post_id AS post_id,
      MAX(pc1.id) OVER (PARTITION BY pc1.post_id) AS max_id
   FROM 
      post_comment pc1
) pc2
JOIN 
   post p ON p.id = pc2.post_id
WHERE 
   pc2.id = pc2.max_id 

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

| post_id | post_title                        | comment_review                        |
|---------|-----------------------------------|---------------------------------------|
| 1       | High-Performance Java Persistence | A must-read for every Java developer! |

Критерии JPA стойкости Blaze

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

К счастью, не только постоянство Blaze может помочь нам сгенерировать этот запрос, но и для Oracle, PostgreSQL, MySQL или SQL Server.

Первым шагом является создание Фабрики построителя критериев с использованием существующей EntityManagerFactory ссылки на объект:

CriteriaBuilderFactory cbf = Criteria
    .getDefault()
    .createCriteriaBuilderFactory(
        entityManagerFactory
    );

И запрос критериев будет выглядеть следующим образом:

List tuples = cbf
    .create(entityManager, Tuple.class)
    .fromSubquery(PostCommentMaxIdCTE.class, "pc2")
        .from(PostComment.class, "pc1")
        .bind("id").select("pc1.id")
        .bind("review").select("pc1.review")
        .bind("postId").select("pc1.post.id")
        .bind("maxId").select("MAX(pc1.id) OVER (PARTITION BY pc1.post.id)")
    .end()
    .joinOn(Post.class, "p", JoinType.INNER)
        .onExpression("p.id = pc2.postId").end()
    .where("pc2.id").eqExpression("pc2.maxId")
    .select("p.id", "post_id")
    .select("p.title", "post_title")
    .select("pc2.review", "comment_review")
    .getResultList();

Обратите внимание, насколько похож API критериев сохранения Blaze по сравнению с исходным SQL-запросом.

При запросе сущностей JPA Blaze Persistence может использовать метамодель сущностей JPA для создания SQL-запроса, но для Производной таблицы, которая строит проекцию с помощью вызова функции MAX Window, нам необходимо предоставить структуру CTE, которая определяет Метамодель для базовой проекции.

По этой причине мы создали класс Post Comment Max Id CTE , который содержит ps2 Проекция производной таблицы:

@CTE
@Entity
public class PostCommentMaxIdCTE {
    @Id
    private Long id;
    private String review;
    private Long postId;
    private Long maxId;
}

Структуры CTE должны быть зарегистрированы в JPA таким же образом, как и юридические лица. Итак, если вы используете Spring Boot, вы можете использовать аннотацию @EntityScan для ссылки на пакет, в котором расположены классы CTE Blaze Persistence:

@EntityScan("com.vladmihalcea.hpjp.jpa.repository.cte")

И при выполнении приведенного выше запроса критериев сохранения Blaze в PostgreSQL выполняется следующий SQL-запрос:

SELECT 
    blazepersi1_.id AS col_0_0_,
    blazepersi1_.title AS col_1_0_,
    blazepersi0_.review AS col_2_0_
FROM (
    SELECT 
        blazepersi0_.id AS col_0_0_,
        blazepersi0_.review AS col_1_0_,
        blazepersi0_.post_id AS col_2_0_,
        MAX(blazepersi0_.id) OVER (
            PARTITION BY blazepersi0_.post_id
        ) AS col_3_0_
        FROM 
            post_comment blazepersi0_
) blazepersi0_(id, review, postid, maxid)
INNER JOIN post blazepersi1_ 
    ON (
        (NULL IS NULL) AND 
        blazepersi1_.id = blazepersi0_.postid 
    )
WHERE  blazepersi0_.id = blazepersi0_.maxid 

И при выполнении того же запроса критериев JPA для сохранения Blaze в MySQL генерируется следующий SQL-запрос:

SELECT 
    blazepersi1_.id AS col_0_0_,
    blazepersi1_.title AS col_1_0_,
    blazepersi0_.review AS col_2_0_
FROM (
    SELECT 
        NULL id,
        NULL review,
        NULL postId,
        NULL maxId
    FROM   dual
    WHERE  1 = 0
    UNION ALL (
        SELECT 
            blazepersi0_.id AS col_0_0_,
            blazepersi0_.review AS col_1_0_,
            blazepersi0_.post_id AS col_2_0_,
            MAX(blazepersi0_.id) OVER (
                PARTITION BY blazepersi0_.post_id
            )                    AS col_3_0_
        FROM post_comment blazepersi0_
    )
) blazepersi0_
INNER JOIN post blazepersi1_
    ON (
        ( NULL IS NULL ) AND 
        blazepersi1_.id = blazepersi0_.postid 
    )
WHERE 
    blazepersi0_.id = blazepersi0_.maxid

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

Вывод

Сохраняемость Blaze-очень полезное дополнение для JPA и Hibernate, поскольку оно позволяет создавать динамические запросы, которые могут использовать нетривиальные функции SQL, такие как:

  • CTE
  • Рекурсивный CTE
  • Функции окна
  • Боковые Соединения

И, поскольку сгенерированные SQL-запросы создаются на основе базового диалекта Hibernate, нам не нужно беспокоиться о переносимости запросов в случае, если нашему приложению необходимо поддерживать несколько систем реляционных баз данных.