Автор оригинала: 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(); CriteriaQuerycriteria = 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(); CriteriaQuerycriteria = 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 extends Topic> sublcass = Post.class; CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuerycriteria = 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 данного Пути выражения может использоваться для фильтрации сущностей по их связанному классу.