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

Как выполнить запрос по типу сущности с помощью API критериев JPA

Узнайте, как выполнять запросы по типу сущности с помощью API критериев JPA, когда корневой тип сущности является базовым классом, а предикат подкласса находится в предложении WHERE.

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

Вступление

Вдохновленный этим сообщением на форуме Hibernate , я решил написать статью, в которой объясню, как можно фильтровать по типу сущности с помощью API критериев.

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

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

Чтобы сохранить эту иерархию сущностей, мы можем использовать наследование JPA , и, как объясняется в этой статье , стратегия наследования SINGLE_TABLE является очень хорошим выбором по умолчанию.

Как следует из названия, SINGLE_TABLE наследование использует одну тему таблицу для хранения сущностей базового класса и подкласса, принадлежащих к этой иерархии наследования:

Для следующих запросов в базе данных будут рассмотрены следующие 2 сущности:

Post post = new Post();
post.setOwner("Vlad");
post.setTitle("Inheritance");
post.setContent("Best practices");

entityManager.persist(post);

Announcement announcement = new Announcement();
announcement.setOwner("Vlad");
announcement.setTitle("Release x.y.z.Final");
announcement.setValidUntil(
    Timestamp.valueOf(LocalDateTime.now().plusMonths(1))
);

entityManager.persist(announcement);

Полиморфные запросы

Одной из функций, предоставляемых наследованием JPA, является возможность извлечения сущностей по их связанному базовому классу. Это называется полиморфным запросом, и следующий запрос выбирает как Сообщение , так и Объявление сущности:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

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

Root root = criteria.from(Topic.class);

criteria.where(
    builder.equal(root.get("owner"), "Vlad")
);

List topics = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(2, topics.size());

Написание запросов API критериев JPA не очень просто. Плагин IDE Codota может помочь вам в написании таких запросов, что повысит вашу производительность.

Для получения более подробной информации о том, как вы можете использовать Codota для ускорения процесса написания запросов API критериев, ознакомьтесь с этой статьей .

Фильтрация подклассов

Теперь, если вы хотите выбрать только подкласс Post , вы можете изменить запрос Root следующим образом:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery criteria = builder.createQuery(Post.class);
Root root = criteria.from(Post.class);

criteria.where(
    builder.equal(root.get("owner"), "Vlad")
);

List posts = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(1, posts.size());

При выполнении этого запроса Hibernate создаст следующий SQL-запрос:

SELECT t.id AS id2_0_,
       t.createdOn AS createdO3_0_,
       t.owner AS owner4_0_,
       t.title AS title5_0_,
       t.content AS content6_0_
FROM   topic t
WHERE  t.DTYPE = 'Post'
  AND  t.owner = 'Vlad'

Обратите внимание, что столбец ТИП используется для фильтрации только объектов Post .

Однако, если мы хотим изменить первый запрос, чтобы мы могли динамически фильтровать объекты Topic по типу подкласса, мы можем использовать метод type класса API Путь Критерии для этой задачи:

Class sublcass = Post.class;

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery criteria = builder.createQuery(Topic.class);
Root root = criteria.from(Topic.class);

criteria.where(
    builder.and(
        builder.equal(root.get("owner"), "Vlad"),
        builder.equal(root.type(), sublcass)
    )
);

List topics = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(1, topics.size());

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

Вывод

API критериев предоставляет множество выражений путей, которые позволяют динамически создавать всевозможные запросы сущностей. Свойство type данного Пути выражения может использоваться для фильтрации сущностей по их связанному классу.